• Stars
    star
    294
  • Rank 140,477 (Top 3 %)
  • Language
    Java
  • Created almost 8 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Benchmarks for Spring Boot startup

Benchmarks for Spring Boot Start Up

TL;DR: if you do nothing else, then make sure you set -XX:TieredStopAtLevel=1 and -noverify as default JVM args in your IDE. They might not be recommended in a long-running, production process, but they have a really big effect on startup time. Get a fast disk (SSD). Also, don’t starve your app of CPU (you need 3 or 4 CPUs to take advantage of some of the parallelization in Spring Boot, 8 is better). And use an up-to-date JVM (although actually 8u131-zulu seems to to be faster than 8u141-openjdk).

Spring and Spring Boot are not slow (after many optimizations, some of which were motivated by data here). Neither component scanning, autoconfiguration, condition processing, nor anything else they do on their own is a significant source of startup time. There is some evidence that most of the cost is associated with JVM overheads, loading and parsing classes. So essentially, if you want more features (more classes) you get slower startup. See in particular the data here for more detail.

Note
if you are looking for more information on memory consumption, as opposed to startup time, please see https://github.com/dsyer/spring-boot-memory-blog.

This project uses JMH to test and report on various the effect of various parameters on Spring Boot startup times. The apps are launched in a forked JVM and the measurements below are all "seconds to start up", which includes the time from the app being executed to the point where it reports "Started" on the console.

Various combinations of things are tested:

  • Spring Boot version, 1.5.1, 1.4.2 and 1.3.8

  • Some JVM flags (e.g. -noverify has an effect -Xmx not so much)

  • Fat jar vs thin jar

  • Exploding the archive and running with JarLauncher or the application main

  • Shading vs Spring Boot fat jars

  • Different application features: a basic web app with actuator, the petclinic and a Spring Cloud config server

  • Hardware (running on bare metal vs AWS for instance)

  • The scaling of startup time with bean count is investigated in more detail in the static module

Note
There is a Spring Boot issue with some comments and more benchmark results. This might be useful to read if you have slow start up times and want some company. Very important: check your anti-virus software and make sure it isn’t slowing down access to jar files.

Spring Boot (and other) issues for follow up:

Running Locally

You need Java 8 and Docker to build all the code cleanly (you can build selectively the bits that don’t depend on Docker if you don’t have it). To run the JsaBenchmark you need an Oracle JVM.

Then do this:

$ ./mvnw clean install
$ (cd benchmarks/; java -jar target/benchmarks.jar)

If you see odd (longer than expected) results in the devtoolsRestart benchmarks, check that you don’t have a ⁠⁠⁠⁠~/.spring-boot-devtools.properties overriding the polling properties.

Running in EC2

Install vagrant and the AWS plugin (https://github.com/mitchellh/vagrant-aws). Then make sure you have an EC2 security group called "vagrant" that allows ssh (open it up for all traffic to be on the safe side). The way the Vagrantfile is configured you need a default VPC in us-east-2 (an account that was created recently will have one by default).

To provide your AWS credentials, define some environment variables:

$ export AWS_ACCESS_KEY=...
$ export AWS_SECRET_KEY=...
$ export AWS_KEYPAIR=...

where AWS_KEYPAIR is the AWS name of the key pair that you have already registered with Amazon. For the configuration to work without changes you also need the private key in a file with read-only (600) permissions called ~/.ssh/id_rsa.${AWS_KEYPAIR}. Then you can

$ ./mvnw clean install
$ cd vagrant
$ vagrant up --provider aws
$ vagrant ssh
> cd /build/benchmarks
> java -jar target/benchmarks.jar

Edit the Vagrantfile to change the instance type and repeat the process. You have to vagrant destroy in between changes of instance type.

If you need to update the benchmarks just mvnw clean install locally and then vagrant reload to sync the changes to the remote box.

Note
I had a lot of issues getting SSH to work. Had to create my own security group, and there was a hiccup with the ssh client wanting to prompt me to change my ~/.ssh/known_hosts. Good luck.

The Minimal Sample

The basic webapp also has a "minimal" version with a stripped down classpath and handpicked configuration classes (instead of @EnableAutoConfiguration). I noticed that logback showed up a lot in the -verbose:class logs so I took it out of the minimal sample. Ditto hibernate-validator (lots of classes initialized compared to the actual feature set I use).

I also tried a super-minimal sample, where I included, instead of a handpicked set of autoconfiguration, the whole set of configuration from the vanilla demo. That’s a bit slower (maybe 150ms) to start up, but not much in it. Also I copied the config files and manually stripped out the @Conditional annotations, so there is no condition processing. This made no appreciable difference (with Spring Boot 1.5.4, although I suspect it might have done with 1.3.8).

Benchmark Results

Summary:

  • It is possible to start a non-trivial app from cold in well under 2 seconds (well under 1 second in a warm JVM).

  • Spring Boot 1.4 and 1.3 are barely distinguishable (1.4 slightly slower but within the error bars). 1.5 is fast too. 2.0 a bit slower, but not much.

  • Devtools restart is much faster than a cold start.

  • Running the app’s main method from an exploded jar is marginally faster.

  • Hardware matters a lot, but affects all apps roughly the same.

  • Spring Boot (and Spring Data) slow things down (but only a bit).

  • Common Data Sharing (CDS) can improve startup a bit (see JsaBenchmark).

N.B. the times reported by the Spring Boot stop watch on the console are always shorter than those measured by the benchmark. The difference is a feature of the hardware used (200ms on faster platforms, 1700ms on slowest) and also the test ("thin" jars have a wider margin because they do a little bit of work checking caches before Spring Boot actually starts).

Spring Boot 2.x

Spring Boot is 2.0 is a bit slower and 2.1 is a bit faster. Early milestones were fine, but degradation continued through the 2.0 development cycle. A lot of progress was made between M7 and RC1, so I suppose it could be worse. Results (before RC2):

Benchmark                                        Mode  Cnt  Score   Error  Units
SpringBoot13xBenchmark.explodedJarLauncher       avgt   10  2.103 ± 0.260   s/op
SpringBoot13xBenchmark.explodedJarMain           avgt   10  1.447 ± 0.038   s/op
SpringBoot13xBenchmark.fatJar                    avgt   10  1.959 ± 0.081   s/op
SpringBoot14xBenchmark.explodedJarLauncher       avgt   10  2.155 ± 0.080   s/op
SpringBoot14xBenchmark.explodedJarMain           avgt   10  1.659 ± 0.053   s/op
SpringBoot14xBenchmark.fatJar                    avgt   10  2.097 ± 0.096   s/op
SpringBoot15xBenchmark.explodedJarLauncher       avgt   10  1.964 ± 0.060   s/op
SpringBoot15xBenchmark.explodedJarMain           avgt   10  1.495 ± 0.045   s/op
SpringBoot15xBenchmark.fatJar                    avgt   10  1.852 ± 0.055   s/op
SpringBoot20xBenchmark.explodedJarLauncher       avgt   10  2.371 ± 0.107   s/op
SpringBoot20xBenchmark.explodedJarMain           avgt   10  1.815 ± 0.053   s/op
SpringBoot20xBenchmark.fatJar                    avgt   10  2.248 ± 0.071   s/op

Spring Boot 2.0.4.RELEASE:

Benchmark                                 Mode  Cnt  Score   Error  Units
PetclinicLatestBenchmark.devtoolsRestart  avgt   10  1.210 ± 0.034   s/op
PetclinicLatestBenchmark.explodedJarMain  avgt   10  4.110 ± 0.092   s/op
PetclinicLatestBenchmark.fatJar           avgt   10  5.599 ± 0.057   s/op
PetclinicLatestBenchmark.noverify         avgt   10  4.892 ± 0.152   s/op

Spring Boot 2.1.0 (snaphots after M1):

Benchmark                                 Mode  Cnt  Score   Error  Units
PetclinicLatestBenchmark.devtoolsRestart  avgt   10  1.208 ± 0.070   s/op
PetclinicLatestBenchmark.explodedJarMain  avgt   10  3.897 ± 0.067   s/op
PetclinicLatestBenchmark.fatJar           avgt   10  4.996 ± 0.032   s/op
PetclinicLatestBenchmark.noverify         avgt   10  4.399 ± 0.029   s/op
PetclinicLatestBenchmark.explodedJarFlags avgt   10  3.325 ± 0.053   s/op

With the new Spring Data LAZY repositories feature (and JPA):

Benchmark                                  Mode  Cnt  Score   Error  Units
PetclinicLatestBenchmark.explodedJarMain   avgt   10  3.751 ± 0.021   s/op
PetclinicLatestBenchmark.fatJar            avgt   10  4.771 ± 0.039   s/op
PetclinicLatestBenchmark.noverify          avgt   10  4.063 ± 0.051   s/op
PetclinicLatestBenchmark.explodedJarFlags  avgt   10  3.095 ± 0.051   s/op

Spring Boot 1.5

There are a few improvements in Spring 1.5 that affect startup time (mainly to do with configuration processing). Here are some highlighted results, where "latest" means Pet Clinic with Boot 1.5:

Benchmark                                   Mode  Cnt  Score   Error  Units
ConfigServerBenchmark.fatJar138             avgt   10  2.959 ± 0.082   s/op
ConfigServerBenchmark.fatJar142             avgt   10  2.786 ± 0.078   s/op
ConfigServerBenchmark.fatJar150             avgt   10  2.668 ± 0.084   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10  2.202 ± 0.111   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10  1.516 ± 0.087   s/op
SpringBoot138Benchmark.fatJar               avgt   10  2.024 ± 0.068   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10  2.210 ± 0.061   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10  1.649 ± 0.089   s/op
SpringBoot142Benchmark.fatJar               avgt   10  2.138 ± 0.081   s/op
SpringBoot150Benchmark.explodedJarLauncher  avgt   10  2.210 ± 0.078   s/op
SpringBoot150Benchmark.explodedJarMain      avgt   10  1.664 ± 0.085   s/op
SpringBoot150Benchmark.fatJar               avgt   10  2.128 ± 0.112   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10  1.116 ± 0.070   s/op
PetclinicBenchmark.explodedJarMain          avgt   10  3.469 ± 0.069   s/op
PetclinicBenchmark.fatJar                   avgt   10  5.003 ± 0.350   s/op
PetclinicBenchmark.noverify                 avgt   10  4.358 ± 0.113   s/op
PetclinicLatestBenchmark.devtoolsRestart    avgt   10  1.056 ± 0.067   s/op
PetclinicLatestBenchmark.explodedJarMain    avgt   10  3.441 ± 0.106   s/op
PetclinicLatestBenchmark.fatJar             avgt   10  4.787 ± 0.112   s/op
PetclinicLatestBenchmark.noverify           avgt   10  4.289 ± 0.070   s/op
MinimalBenchmark.explodedJarMain            avgt   10  1.250 ± 0.029   s/op
MinimalBenchmark.fatJar                     avgt   10  1.579 ± 0.072   s/op

The configserver benefits by a few hundred millseconds. The vanilla demo (web plus actuator) interestingly does not. The Pet Clinic also gets a couple of hundred milliseconds boost, but not so much in the exploded jar case (which mimic what people get in the IDE). The minimal sample is quite a bit zippier.

Host: tower, i7, 3.4GHz, 32G RAM, SSD

New Dell desktop

 Benchmark                                   Mode  Cnt  Score   Error  Units
 ConfigServerBenchmark.devtoolsRestart       avgt   10  1.034 ± 0.201   s/op
 ConfigServerBenchmark.explodedJarMain       avgt   10  3.271 ± 0.234   s/op
 ConfigServerBenchmark.fatJar138             avgt   10  3.267 ± 0.173   s/op
 ConfigServerBenchmark.fatJar142             avgt   10  3.423 ± 0.160   s/op
 PetclinicBenchmark.devtoolsRestart          avgt   10  1.387 ± 0.399   s/op
 PetclinicBenchmark.explodedJarMain          avgt   10  5.491 ± 0.357   s/op
 PetclinicBenchmark.explodedShadedMain       avgt   10  5.266 ± 0.332   s/op
 PetclinicBenchmark.fatJar                   avgt   10  6.130 ± 0.271   s/op
 PetclinicBenchmark.noverify                 avgt   10  5.430 ± 0.217   s/op
 PetclinicBenchmark.shaded                   avgt   10  7.975 ± 0.307   s/op
 SpringBoot138Benchmark.explodedJarLauncher  avgt   10  2.764 ± 0.149   s/op
 SpringBoot138Benchmark.explodedJarMain      avgt   10  2.004 ± 0.074   s/op
 SpringBoot138Benchmark.fatJar               avgt   10  2.589 ± 0.107   s/op
 SpringBoot142Benchmark.explodedJarLauncher  avgt   10  2.873 ± 0.105   s/op
 SpringBoot142Benchmark.explodedJarMain      avgt   10  2.229 ± 0.083   s/op
 SpringBoot142Benchmark.fatJar               avgt   10  2.677 ± 0.209   s/op
 SpringBootThinBenchmark.basic138Thin        avgt   10  2.474 ± 0.134   s/op
 SpringBootThinBenchmark.basic142Thin        avgt   10  2.764 ± 0.160   s/op
 MinimalBenchmark.explodedJarMain            avgt   10  1.602 ± 0.120   s/op
 MinimalBenchmark.fatJar                     avgt   10  1.901 ± 0.089   s/op
 JsaBenchmark.basicThin                      avgt   10  5.725 ± 0.460   s/op
 JsaBenchmark.sharedClasses                  avgt   10  4.302 ± 0.203   s/op
 JsaBenchmark.thinMain                       avgt   10  4.808 ± 0.161   s/op

Unused Thymeleaf Features

Excluding groovy and thymeleaf layout (unused dependencies) from the Pet Clinic had a big effect on the startup time, but oddly only in the exploded main test:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
	<exclusions>
		<exclusion>
			<groupId>org.codehaus.groovy</groupId>
			<artifactId>groovy</artifactId>
		</exclusion>
		<exclusion>
			<groupId>nz.net.ultraq.thymeleaf</groupId>
			<artifactId>thymeleaf-layout-dialect</artifactId>
		</exclusion>
	</exclusions>
</dependency>

Before:

PetclinicBenchmark.explodedJarMain  avgt   10  5.491 ± 0.357   s/op

and after:

PetclinicBenchmark.explodedJarMain  avgt   10  4.894 ± 0.186   s/op

More JVM Options

Setting -XX:TieredStopAtLevel=1 was quite a big boost, so we set that permanently in the benchmarks (the rest of the results pre-date this change):

Benchmark                                   Mode  Cnt  Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10  0.842 ± 0.047   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10  2.451 ± 0.100   s/op
ConfigServerBenchmark.fatJar138             avgt   10  3.002 ± 0.091   s/op
ConfigServerBenchmark.fatJar142             avgt   10  2.936 ± 0.101   s/op
MinimalBenchmark.explodedJarMain            avgt   10  1.268 ± 0.077   s/op
MinimalBenchmark.fatJar                     avgt   10  1.537 ± 0.078   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10  1.107 ± 0.054   s/op
PetclinicBenchmark.explodedJarMain          avgt   10  3.618 ± 0.149   s/op
PetclinicBenchmark.fatJar                   avgt   10  5.040 ± 0.229   s/op
PetclinicBenchmark.noverify                 avgt   10  4.402 ± 0.149   s/op
ShadedBenchmark.explodedShadedMain          avgt   10  3.764 ± 0.085   s/op
ShadedBenchmark.shaded                      avgt   10  5.871 ± 0.123   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10  2.224 ± 0.083   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10  1.542 ± 0.044   s/op
SpringBoot138Benchmark.fatJar               avgt   10  2.102 ± 0.126   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10  2.232 ± 0.067   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10  1.684 ± 0.087   s/op
SpringBoot142Benchmark.fatJar               avgt   10  2.146 ± 0.080   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10  1.908 ± 0.120   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10  2.081 ± 0.088   s/op
SpringBootThinBenchmark.petclinicThin       avgt   10  4.038 ± 0.267   s/op
JsaBenchmark.basicThin                      avgt   10  4.230 ± 0.216   s/op
JsaBenchmark.sharedClasses                  avgt   10  3.124 ± 0.125   s/op
JsaBenchmark.thinMain                       avgt   10  3.573 ± 0.229   s/op

This wouldn’t be a sensible setting for a long-running process in production, but it seems to have a positive effect on startup, so at dev time it’s a good idea.

Class Data Sharing (CDS)

Class Data Sharing is a JDK feature (since Java 5). Some people have reported success in improving startup time with simple apps (e.g. see this blog).

CDS is not a "standard" JVM option, but it works for core JDK classes with OpenJDK. You have to use the Oracle JDK for the JsaBenchmark which also uses application class data sharing. IBM and Open J9 have their own version of the same thing. Zulu works with the vanilla CdsBenchmark.

There are data for a JsaBenchmark that uses commercial features in the Oracle JDK, and also for a "vanilla" CdsBenchmark (not using commercial features).

With 8u151-oracle:

Benchmark                   Mode  Cnt  Score   Error  Units
CdsBenchmark.sharedClasses  avgt   10  2.871 ± 0.088   s/op
CdsBenchmark.thinMain       avgt   10  2.933 ± 0.154   s/op
JsaBenchmark.sharedClasses  avgt   10  2.893 ± 0.068   s/op
JsaBenchmark.thinMain       avgt   10  2.898 ± 0.085   s/op

With 8u152-zulu:

Benchmark                   Mode  Cnt  Score   Error  Units
CdsBenchmark.sharedClasses  avgt   10  2.854 ± 0.042   s/op
CdsBenchmark.thinMain       avgt   10  2.837 ± 0.072   s/op

So there is really nothing in it (which is what I remembered from the JDK9 results I looked at as well). The difference in the old results is probably entirely explained by the effect of -noverify, which I forgot to include in the non-shared classes execution.

This is a PetClinic with Boot 1.4.6. There’s no reason to put any effort into exploring the effect with other versions and other apps based on this data.

Java 10 has app class sharing as a new (non-commercial) feature. It is slower in general than Java 8, but the class sharing does improve startup times:

Benchmark                         (sample)  Mode  Cnt  Score   Error  Units
Java10CdsBenchmark.sharedClasses     older  avgt   10  2.780 ± 0.071   s/op
Java10CdsBenchmark.sharedClasses    latest  avgt   10  3.407 ± 0.223   s/op
Java10CdsBenchmark.thinMain          older  avgt   10  3.217 ± 0.783   s/op
Java10CdsBenchmark.thinMain         latest  avgt   10  3.747 ± 1.026   s/op

The "older" is Boot 1.5, and "latest" is 2.0, so the effect is more dramatic for Boot 1.5. Things are a bit better with Java 11 (apparently there was a bug in Java 10 that made it slower on startup overall). See the static module for more data.

Logback

By switching between Logback and other libraries in the minimal sample we can test the effect of the logging library. We did this by removing the logging starter completely, and adding in commons-logging (which drops down to plain JDK logging by default). The results show that logback slows down the fat jar compared to plain JDK logging, but not the exploded main. The effect is only 100ms. Here are the data (with all the rest of the test set up as per the previous section):

MinimalBenchmark.explodedJarMain            avgt   10  1.292 ± 0.105   s/op
MinimalBenchmark.fatJar                     avgt   10  1.620 ± 0.082   s/op

Log4j2 is actually slower:

MinimalBenchmark.explodedJarMain            avgt   10  1.425 ± 0.098   s/op
MinimalBenchmark.fatJar                     avgt   10  1.820 ± 0.069   s/op

Remember there are only about 40 lines of logging in these tests. It might well be that logback or log4j2 performs much better under load, and this test is just for initialization time really.

EhCache

Stripping everything else back reveals EhCache initialization as a hotspot in the Pet Clinic (it shows up in YourKit when you launch in "trace" mode). Removing it from the classpath boosts startup by a couple of hundred milliseconds:

PetclinicBenchmark.devtoolsRestart  avgt   10  1.095 ± 0.086   s/op
PetclinicBenchmark.explodedJarMain  avgt   10  3.465 ± 0.145   s/op
PetclinicBenchmark.fatJar           avgt   10  4.827 ± 0.241   s/op
PetclinicBenchmark.noverify         avgt   10  4.249 ± 0.116   s/op

If you add -noverify as well, you will see the startup time in an exploded jar (so in the IDE) at a little more than 3s (down from over 6s initially). Note that EhCache is not used by the app unless the "production" profile is enabled, so you can switch it off just by disabling the profile. The benchmarks allow you to do that by setting -Dbench.args, e.g:

$ cd benchmarks/
$ java -Dbench.args='-Dspring.profiles.active=default' -Ddebug \
  -jar target/benchmarks.jar PetclinicBenchmark

Actuator

Removing the actuator actually changes the features of the application, so in some ways it’s an unfair test, but on the other hand, if you only want to test the business features you might not care if the actuator was not there on startup. The results are promising (cumulative on top of disabling ehcache):

PetclinicBenchmark.devtoolsRestart  avgt   10  0.781 ± 0.056   s/op
PetclinicBenchmark.explodedJarMain  avgt   10  2.845 ± 0.089   s/op
PetclinicBenchmark.fatJar           avgt   10  4.083 ± 0.192   s/op
PetclinicBenchmark.noverify         avgt   10  3.566 ± 0.146   s/op

Hibernate Validator

What if we ditch the validator (also shows up as a hotspot in YourKit)? Again this is cutting into application features - you even have to edit the Pet Clinic quite heavily to get it to work without validation. Results are good though, so maybe we could make it lazy somehow:

Benchmark                           Mode  Cnt  Score   Error  Units
PetclinicBenchmark.devtoolsRestart  avgt   10  0.724 ± 0.047   s/op
PetclinicBenchmark.explodedJarMain  avgt   10  2.670 ± 0.136   s/op
PetclinicBenchmark.fatJar           avgt   10  3.682 ± 0.097   s/op
PetclinicBenchmark.noverify         avgt   10  3.213 ± 0.146   s/op

Thymeleaf and "Unused Beans"

Initializing a Jackson ObjectMapper is the next hotspot after EhCache is disabled. It turns out that this is a side effect of initializing Thymeleaf. It happens in a class initializer in SpringTemplateEngine, so there’s no way to switch it off with lazy beans in Spring, other than to remove Jackson from the classpath.

Even if we removed Thymeleaf, and replaced it with Mustache for instance, Jackson is needed by the actuator. So we would pay the startup cost anyway, despite the fact that it isn’t used. This raises the question of why we need to initialize these things at all. They aren’t going to be used until a request is processed.

Here’s the result of replacing Thymeleaf with Mustache (on top of all the other improvements above) and excluding Jackson:

Benchmark                           Mode  Cnt  Score   Error  Units
PetclinicBenchmark.devtoolsRestart  avgt   10  0.727 ± 0.052   s/op
PetclinicBenchmark.explodedJarMain  avgt   10  2.577 ± 0.091   s/op
PetclinicBenchmark.fatJar           avgt   10  3.514 ± 0.107   s/op
PetclinicBenchmark.noverify         avgt   10  3.046 ± 0.142   s/op

There is a small improvement, but it’s not clear that it has precisely the origin we attribute using the reasoning above. The reasoning behind that assertion is that if we do a background initialization of Thymeleaf in an ApplicationInitializer (same pattern as BackgroundPreinitializer from Spring Boot) then there is no improvement in startup time, despite the fact that Jackson and Thymeleaf are already initialized. It is interesting that removing Thymeleaf completely has an impact, but it must be something other than simply the static initializer. Also notable is that we saw an improvement above when we removed Hibernate Validator from the project, but this is also pre-initialized by Spring Boot, so the inefficiency is not in the initialization there either.

A lot of components in a Spring Boot app are in the category of "needed at some point, but maybe not even for all requests, and definitely not at startup". Another one that shows up in YourKit on startup is the Webjars Locator. You only get <100ms improvement in startup time by leaving it out (and it changes the behaviour of the application, so you want it in there at runtime), but you don’t need it until after startup.

Jackson

Removing Jackson means removing the actuators as well, as things stand. But it has quite a dramatic effect (probably Thymeleaf has no impact at all and we are just seeing Jackson in the results above). Results:

Benchmark                           Mode  Cnt  Score   Error  Units
PetclinicBenchmark.devtoolsRestart  avgt   10  0.851 ± 0.018   s/op
PetclinicBenchmark.explodedJarMain  avgt   10  2.823 ± 0.075   s/op
PetclinicBenchmark.fatJar           avgt   10  3.895 ± 0.039   s/op
PetclinicBenchmark.noverify         avgt   10  3.397 ± 0.060   s/op

These should be compared with the same thing but fully loaded (not the latest result in the previous paragraph). Here’s a reminder of what they said:

Benchmark                                   Mode  Cnt  Score   Error  Units
PetclinicBenchmark.devtoolsRestart          avgt   10  1.107 ± 0.054   s/op
PetclinicBenchmark.explodedJarMain          avgt   10  3.618 ± 0.149   s/op
PetclinicBenchmark.fatJar                   avgt   10  5.040 ± 0.229   s/op
PetclinicBenchmark.noverify                 avgt   10  4.402 ± 0.149   s/op

That’s pretty huge!

Spring 5.0 Component Index

Spring 5.0 has an annotation processor that creates an index of components on startup. It is easy to set up (you just put spring-context-indexer on the classpath), but since it only indexes a component scan, which is a pretty small part of a trivial Spring Boot application like the ones we are testing, it has no discernable effect at runtime.

Host: xerus, Core2 (32bit), 2.66GHz, 4G RAM, HDD

>5 year old Dell desktop

Benchmark                                   Mode  Cnt   Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10   2.041 ± 0.173   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10   6.930 ± 0.228   s/op
ConfigServerBenchmark.fatJar138             avgt   10   7.395 ± 0.203   s/op
ConfigServerBenchmark.fatJar142             avgt   10   7.439 ± 0.247   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10   2.868 ± 0.195   s/op
PetclinicBenchmark.fatJar                   avgt   10  13.579 ± 0.222   s/op
PetclinicBenchmark.noverify                 avgt   10  12.186 ± 0.373   s/op
JsaBenchmark.basicThin                      avgt   10  10.830 ± 0.443   s/op
JsaBenchmark.sharedClasses                  avgt   10   8.158 ± 0.217   s/op
JsaBenchmark.thinMain                       avgt   10   9.568 ± 0.488   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10   5.821 ± 0.191   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10   4.365 ± 0.117   s/op
SpringBoot138Benchmark.fatJar               avgt   10   5.550 ± 0.132   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10   6.238 ± 0.225   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10   4.840 ± 0.137   s/op
SpringBoot142Benchmark.fatJar               avgt   10   6.238 ± 0.560   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10   5.248 ± 0.130   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10   5.715 ± 0.115   s/op

Host: carbon, i7, 2.6GHz, 4G RAM, SSD

One year old Lenovo X1 laptop.

Benchmark                                   Mode  Cnt  Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10  1.458 ± 0.256   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10  5.217 ± 0.280   s/op
ConfigServerBenchmark.fatJar138             avgt   10  5.651 ± 0.221   s/op
ConfigServerBenchmark.fatJar142             avgt   10  5.812 ± 0.163   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10  1.952 ± 0.249   s/op
PetclinicBenchmark.fatJar                   avgt   10  9.973 ± 0.637   s/op
PetclinicBenchmark.noverify                 avgt   10  9.312 ± 0.350   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10  4.696 ± 0.242   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10  3.599 ± 0.192   s/op
SpringBoot138Benchmark.fatJar               avgt   10  4.427 ± 0.249   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10  4.926 ± 0.313   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10  3.824 ± 0.150   s/op
SpringBoot142Benchmark.fatJar               avgt   10  4.814 ± 0.199   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10  4.370 ± 0.200   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10  4.796 ± 0.165   s/op

Host: dsyer, i7, 2.7GHz, 4G RAM, SSD

Slightly knackered old Lenovo X220 laptop.

Benchmark                                   Mode  Cnt   Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10   1.568 ± 0.253   s/op
ConfigServerBenchmark.fatJar138             avgt   10   6.267 ± 0.274   s/op
ConfigServerBenchmark.fatJar142             avgt   10   6.426 ± 0.398   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10   5.880 ± 0.326   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10   2.053 ± 0.226   s/op
PetclinicBenchmark.fatJar                   avgt   10  11.241 ± 0.365   s/op
PetclinicBenchmark.noverify                 avgt   10  10.726 ± 0.526   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10   5.526 ± 0.341   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10   4.190 ± 0.201   s/op
SpringBoot138Benchmark.fatJar               avgt   10   5.325 ± 0.206   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10   5.958 ± 0.190   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10   4.589 ± 0.258   s/op
SpringBoot142Benchmark.fatJar               avgt   10   5.638 ± 0.269   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10   5.082 ± 0.307   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10   5.731 ± 0.211   s/op

Host: t2.small, 1CPU, 1G RAM, SSD

Benchmark                                   Mode  Cnt   Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10   1.760 ± 0.236   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10   8.597 ± 0.218   s/op
ConfigServerBenchmark.fatJar138             avgt   10   9.114 ± 0.387   s/op
ConfigServerBenchmark.fatJar142             avgt   10   9.092 ± 0.358   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10   2.709 ± 0.180   s/op
PetclinicBenchmark.fatJar                   avgt   10  15.277 ± 0.414   s/op
PetclinicBenchmark.noverify                 avgt   10  14.011 ± 0.340   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10   7.327 ± 0.223   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10   5.684 ± 0.099   s/op
SpringBoot138Benchmark.fatJar               avgt   10   7.017 ± 0.218   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10   7.825 ± 0.193   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10   6.150 ± 0.160   s/op
SpringBoot142Benchmark.fatJar               avgt   10   7.372 ± 0.204   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10   7.055 ± 0.175   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10   7.553 ± 0.255   s/op

Host: t2.large, 2CPU, 8G RAM, SSD

Benchmark                                   Mode  Cnt  Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10  1.071 ± 0.170   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10  4.504 ± 0.250   s/op
ConfigServerBenchmark.fatJar138             avgt   10  4.706 ± 0.110   s/op
ConfigServerBenchmark.fatJar142             avgt   10  4.730 ± 0.159   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10  1.555 ± 0.198   s/op
PetclinicBenchmark.fatJar                   avgt   10  8.181 ± 0.396   s/op
PetclinicBenchmark.noverify                 avgt   10  7.393 ± 0.304   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10  3.857 ± 0.141   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10  3.113 ± 0.145   s/op
SpringBoot138Benchmark.fatJar               avgt   10  3.729 ± 0.088   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10  4.074 ± 0.170   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10  3.468 ± 0.159   s/op
SpringBoot142Benchmark.fatJar               avgt   10  3.916 ± 0.207   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10  3.935 ± 0.176   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10  4.250 ± 0.208   s/op

Host: m4.large, 2CPU, 8G RAM, SSD

Benchmark                                   Mode  Cnt   Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10   1.638 ± 0.255   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10   7.222 ± 0.153   s/op
ConfigServerBenchmark.fatJar138             avgt   10   7.641 ± 0.212   s/op
ConfigServerBenchmark.fatJar142             avgt   10   7.616 ± 0.182   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10   2.502 ± 0.314   s/op
PetclinicBenchmark.explodedJarMain          avgt   10  11.447 ± 0.209   s/op
PetclinicBenchmark.fatJar                   avgt   10  12.835 ± 0.341   s/op
PetclinicBenchmark.noverify                 avgt   10  11.833 ± 0.438   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10   6.195 ± 0.193   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10   4.939 ± 0.217   s/op
SpringBoot138Benchmark.fatJar               avgt   10   5.893 ± 0.190   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10   6.471 ± 0.126   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10   5.360 ± 0.209   s/op
SpringBoot142Benchmark.fatJar               avgt   10   6.302 ± 0.199   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10   6.013 ± 0.159   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10   6.437 ± 0.301   s/op

Host: r3.large, 2CPU, 15G RAM, SSD

Benchmark                                   Mode  Cnt   Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10   1.874 ± 0.344   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10   7.967 ± 0.270   s/op
ConfigServerBenchmark.fatJar138             avgt   10   8.375 ± 0.229   s/op
ConfigServerBenchmark.fatJar142             avgt   10   8.541 ± 0.317   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10   2.707 ± 0.333   s/op
PetclinicBenchmark.explodedJarMain          avgt   10  12.403 ± 0.385   s/op
PetclinicBenchmark.fatJar                   avgt   10  13.956 ± 0.439   s/op
PetclinicBenchmark.noverify                 avgt   10  13.023 ± 0.382   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10   6.802 ± 0.272   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10   5.370 ± 0.341   s/op
SpringBoot138Benchmark.fatJar               avgt   10   6.609 ± 0.328   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10   7.157 ± 0.233   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10   5.946 ± 0.133   s/op
SpringBoot142Benchmark.fatJar               avgt   10   6.855 ± 0.173   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10   6.527 ± 0.262   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10   7.175 ± 0.286   s/op

Host: aw-rmbp, i7, 2.3GHz, 16GB RAM, SSD

MacBook Pro (Retina, 15-inch, Late 2013) running OS X Yosemite (10.10.5) and Java 1.8.0_102

Benchmark                                   Mode  Cnt  Score   Error  Units
ConfigServerBenchmark.devtoolsRestart       avgt   10  0.999 ± 0.078   s/op
ConfigServerBenchmark.explodedJarMain       avgt   10  3.747 ± 0.177   s/op
ConfigServerBenchmark.fatJar138             avgt   10  3.810 ± 0.158   s/op
ConfigServerBenchmark.fatJar142             avgt   10  3.846 ± 0.049   s/op
PetclinicBenchmark.devtoolsRestart          avgt   10  1.431 ± 0.171   s/op
PetclinicBenchmark.fatJar                   avgt   10  6.995 ± 0.299   s/op
PetclinicBenchmark.noverify                 avgt   10  6.292 ± 0.308   s/op
SpringBoot138Benchmark.explodedJarLauncher  avgt   10  3.256 ± 0.104   s/op
SpringBoot138Benchmark.explodedJarMain      avgt   10  2.518 ± 0.074   s/op
SpringBoot138Benchmark.fatJar               avgt   10  3.012 ± 0.046   s/op
SpringBoot142Benchmark.explodedJarLauncher  avgt   10  3.374 ± 0.082   s/op
SpringBoot142Benchmark.explodedJarMain      avgt   10  2.738 ± 0.040   s/op
SpringBoot142Benchmark.fatJar               avgt   10  3.247 ± 0.109   s/op
SpringBootThinBenchmark.basic138Thin        avgt   10  3.035 ± 0.083   s/op
SpringBootThinBenchmark.basic142Thin        avgt   10  3.271 ± 0.093   s/op

Finer Grained Timing

There’s a tool in the JDK called jcmd that prints out a load of useful stuff about a running process. E.g:

$ jcmd <PID> PerfCounter.print

where <PID> is the process id. The petclinic (on "tower") has these times in ns (highlights):

...
sun.classloader.findClassTime=2051631382
sun.classloader.parentDelegationTime=220795236
sun.cls.appClassLoadTime=2315218900
sun.cls.classInitTime=1058084598
sun.cls.classLinkedTime=1142874583
sun.cls.classVerifyTime=1037315846
sun.cls.defineAppClassTime=946831715
sun.cls.lookupSysClassTime=28253463
sun.cls.parseClassTime=1055187254
sun.cls.sharedClassLoadTime=581133
sun.cls.sysClassLoadTime=223416957
sun.urlClassLoader.readClassBytesTime=299665148
...

So you can immediately save up to a second by using -noverify (probably not advisable in production, but the jury is out on that). The benchmark results confirm this the saving is actually only 700ms, but that’s a start.

Other things that stick out are

  • findClassTime - time spent working out where the damn things are, loading the bytes, parsing the bytes and defining them to build Class instances (2052ms).

  • readClassBytesTime - time from when you have a reference to where the bytes are to the time you have read them in (300ms).

Also interesting (not shown above) is java.cls.loadedClasses, at 10660. Perhaps this is over optimistic but maybe some chunk of them is being touched by accident that aren’t necessary and they can be skipped, or deferred.

Log Analysis

More Repositories

1

spring-boot-angular

Quick tutorial on how to create a Spring Boot app with Angular front end using Maven and modern front end tooling
TypeScript
342
star
2

spring-boot-aspectj

Sample applications showing how to use AspectJ with Spring Boot
Java
164
star
3

spring-boot-memory-blog

Spring Boot Memory Performance Blog
152
star
4

sparklr-boot

Demo OAuth2 provider with Spring Boot
Java
137
star
5

spring-boot-micro-apps

Java
131
star
6

kubernetes-intro

Shell
119
star
7

spring-boot-legacy

Support for legacy (Servlet 2.5) apps in Spring Boot
Java
100
star
8

spring-boot-allocations

Java
99
star
9

reactive-notes

Java
89
star
10

spring-security-architecture

High level documentation for Spring Security and users of Spring Boot
79
star
11

inner-loop-boot-k8s

Java
65
star
12

spring-boot-ratpack

Spring Boot wrapper for Ratpack
Java
53
star
13

spring-boot-jersey

Jersey autoconfig support for Spring Boot
Java
51
star
14

spring-boot-java-10

Java
36
star
15

spring-security-rsa

Java
30
star
16

spring-boot-operator

Go
29
star
17

main-branch-switch

Scripts for switching a Github repository to use a "main" branch
Shell
25
star
18

mustache-sample

Java
24
star
19

spring-boot-js-demo

These samples explore the different options that Spring Boot developers have for using Javascript and CSS on the client (browser) side of their application.
Java
23
star
20

rsocket-test-server

Java
23
star
21

spring-boot-sample-data-eclipselink

Java
23
star
22

dist-tx

Source code for distributed transactions article from 2008, updated to use Spring Boot
Java
19
star
23

dependency-hell

Short article on a dependency management issue with Maven 3.3
Shell
16
star
24

docker-services

Dockerfile
15
star
25

spring-oauth2-integration-tests

Integration tests and demo apps for OAuth2 with Spring
Java
14
star
26

spring-boot-mustache

Spring Boot autoconfiguration for JMutsache (web and nonweb template rendering)
Java
14
star
27

spring-boot-auto-reflect

Java
13
star
28

spring-boot-spa

Shell
12
star
29

spring-batch-gemfire

Gemfire integration for Spring Batch
Java
12
star
30

protobuf-wasm

Shell
12
star
31

presos

Presentations for conferences and user groups etc.
HTML
11
star
32

frontend-microservices

Java
11
star
33

auth-server-client

Shell
11
star
34

log4j-utils

Log4j Utilities
Java
10
star
35

spring-todo-mvc

Java
10
star
36

skaffold-devtools-demo

Java
9
star
37

spring-batch-gridgain

GridGain integration for Spring Batch
Java
8
star
38

spring-wasm-demo

C
8
star
39

native-grpc

Java
7
star
40

spring-boot-initializer

Java
6
star
41

payments-demo

Java
6
star
42

spring-security-oauth2-hydra

Java
6
star
43

spring-boot-aot

Java
6
star
44

cloud-middleware-blog

Blog text and sample project for article on spring.io in 2015
Java
6
star
45

qcon-workshop-2016

Shell
6
star
46

hello-wasm

C
5
star
47

spring-boot-features

Java
5
star
48

http-amqp-tunnel

Tunnel HTTP over AMQP using Spring Integration
Java
5
star
49

spring-boot-lazy-actuator

Java
5
star
50

spring-spreadsheet

Spring spreadsheet support (Excel, OpenOffice etc.)
Java
5
star
51

gs-spring-boot-docker

Spring Boot with Docker :: Learn how to create a Docker container from a Spring Boot application with Maven or Gradle
Java
5
star
52

two-factor

Shell
4
star
53

spring-boot-factory-aspects

Java
4
star
54

java-function-benchmarks

Java
4
star
55

riff-http

Shell
4
star
56

spring-data-simple

Simple Repository metadata for repositories created "manually" (i.e. not auto-generated)
Java
4
star
57

stream-startup

Java
4
star
58

jlink-petclinic

Dockerfile
4
star
59

spring-batch-grid

Grid adapters for Spring Batch
Java
4
star
60

lambda-aws-demo

Demo for AWS custom runtime native image
Java
4
star
61

nix-config

Emacs Lisp
4
star
62

async-wasm

C
4
star
63

refresh

Java
4
star
64

cflogin

Authorization Server for "fully-leaded" Cloud Foundry access tokens
Java
3
star
65

spring-config-operator

Go
3
star
66

openapi-rust

Rust
3
star
67

spring-boot-legacy-agent

Provides a Java agent that allows an app that was compiled with Spring Boot 2.0 to run with earlier versions.
Java
3
star
68

spring-restdocs-messaging

Java
2
star
69

spring-boot-proto

Java
2
star
70

native-config-server

Dockerfile
2
star
71

petflix

Java
2
star
72

pod-mutating-webhook

Go
2
star
73

simple-gateway

simple-gateway
2
star
74

spring-petclinic-jpa

Java
2
star
75

gateway-wasm

Java
2
star
76

spring-boot-archetypes

Automation scripts for publishing Spring Boot Samples as Maven archetypes
Shell
2
star
77

SpringCloudWorkshop

TSQL
2
star
78

spring-controller

Java
2
star
79

cf-memory-tests

Java
2
star
80

openapi-demo

Java
2
star
81

sagan-wiki

2
star
82

nix-demo

Dockerfile
2
star
83

spring-xd-module-runner

2
star
84

spring-cloud-stream-binder-servlet

Java
2
star
85

sso

Java
2
star
86

js-utils

JavaScript
1
star
87

ubuntu-make-vm-images

Shell
1
star
88

teavm-demo

Java
1
star
89

nix-cloud

Shell
1
star
90

vanilla-orm

Java
1
star
91

spring-boot-bindings

Go
1
star
92

dotnet-test

Get dotnet working on Linux
Dockerfile
1
star
93

github-admin

Java
1
star
94

doppler-metrics-sample

Java
1
star
95

azure-custom-handler

HCL
1
star
96

spring-cloud-deployer-thin

Java
1
star
97

spring-service-operator

Go
1
star
98

hello-nix-java

Java
1
star
99

hello-rust

Java
1
star
100

dispatcher-servlet-container

Java
1
star