Speed up Spring Boot startup time

132,712

Solution 1

Spring Boot does a lot of auto-configuration that may not be needed. So you may want to narrow down only auto-configuration that is needed for your app. To see full list of auto-configuration included, just run logging of org.springframework.boot.autoconfigure in DEBUG mode (logging.level.org.springframework.boot.autoconfigure=DEBUG in application.properties). Another option is to run spring boot application with --debug option: java -jar myproject-0.0.1-SNAPSHOT.jar --debug

There would be something like this in output:

=========================
AUTO-CONFIGURATION REPORT
=========================

Inspect this list and include only autoconfigurations you need:

@Configuration
@Import({
        DispatcherServletAutoConfiguration.class,
        EmbeddedServletContainerAutoConfiguration.class,
        ErrorMvcAutoConfiguration.class,
        HttpEncodingAutoConfiguration.class,
        HttpMessageConvertersAutoConfiguration.class,
        JacksonAutoConfiguration.class,
        ServerPropertiesAutoConfiguration.class,
        PropertyPlaceholderAutoConfiguration.class,
        ThymeleafAutoConfiguration.class,
        WebMvcAutoConfiguration.class,
        WebSocketAutoConfiguration.class,
})
public class SampleWebUiApplication {

Code was copied from this blog post.

Solution 2

The most voted answer so far is not wrong, but it doesn't go into the depth I like to see and provides no scientific evidence. The Spring Boot team went through an exercise for reducing startup time for Boot 2.0, and ticket 11226 contains a lot of useful information. There is also a ticket 7939 open to adding timing information to condition evaluation, but it doesn't seem to have a specific ETA.

The most useful, and methodical approach for debugging Boot startup has been done by Dave Syer. https://github.com/dsyer/spring-boot-startup-bench

I had a similar use case as well, so I took Dave's approach of micro-benchmarking with JMH and ran with it. The result is the boot-benchmark project. I designed it such that it can be used to measure startup time for any Spring Boot application, using the executable jar produced by bootJar (previously called bootRepackage in Boot 1.5) Gradle task. Feel free to use it and provide feedback.

My findings are as follows:

  1. CPU matters. A lot.
  2. Starting the JVM with -Xverify:none helps significantly.
  3. Excluding unnecessary autoconfigurations helps.
  4. Dave recommended JVM argument -XX:TieredStopAtLevel=1, but my tests didn't show significant improvement with that. Also, -XX:TieredStopAtLevel=1 would probably slow down your first request.
  5. There have been reports of hostname resolution being slow, but I didn't find it to be a problem for the apps I tested.

Solution 3

Spring Boot 2.2.M1 has added feature to support Lazy Initialization in Spring Boot.

By default, when an application context is being refreshed, every bean in the context is created and its dependencies are injected. By contrast, when a bean definition is configured to be initialized lazily it will not be created and its dependencies will not be injected until it’s needed.

Enabling Lazy Initialization Set spring.main.lazy-initialization to true

When to Enable Lazy Initialization

lazy initialization can offer significant improvements in start up time but there are some notable downsides too and it’s important to enable it with care

For more details please check Doc

Update:

Spring Boot Spring Boot 2.4.0 - Startup Endpoint

Spring Boot 2.4.0 has added a new Startup endpoint that can be used to identify beans that are taking longer than expected to start. You can get more details about the Application Startup tracking here

Solution 4

As described in this question/answer, I think the best approach is to instead of adding only those you think you need, exclude the dependencies you know you don't need.

See: Minimise Spring Boot Startup Time

In summary:

You can see what is going on under the covers and enable debug logging as simple as specifying --debug when starting the application from the command-line. You can also specify debug=true in your application.properties.

Also, you can set the logging level in application.properties as simple as:

logging.level.org.springframework.web: DEBUG logging.level.org.hibernate: ERROR

If you detect an auto-configured module you don't want, it can be disabled. The docs for this can be found here: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#using-boot-disabling-specific-auto-configuration

An example would look like:

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}

Solution 5

Well there is entire list of possible actions described here: https://spring.io/blog/2018/12/12/how-fast-is-spring

I will put the most important notes from Spring side (adjusted a little bit):

  • Classpath exclusions from Spring Boot web starters:
    • Hibernate Validator
    • Jackson (but Spring Boot actuators depend on it). Use Gson if you need JSON rendering (only works with MVC out of the box).
    • Logback: use slf4j-jdk14 instead
  • Use the spring-context-indexer. It’s not going to add much, but every little helps.
  • Don’t use actuators if you can afford not to.
  • Use Spring Boot 2.1 and Spring 5.1. Switch to 2.2 and 5.2 when they are available.
  • Fix the location of the Spring Boot config file(s) with spring.config.location (command line argument or System property etc.). Example for testing in IDE: spring.config.location=file://./src/main/resources/application.properties.
  • Switch off JMX if you don’t need it with spring.jmx.enabled=false (this is the default in Spring Boot 2.2)
  • Make bean definitions lazy by default. There’s a new flag spring.main.lazy-initialization=true in Spring Boot 2.2 (use LazyInitBeanFactoryPostProcessor for older Spring).
  • Unpack the fat jar and run with an explicit classpath.
  • Run the JVM with -noverify. Also consider -XX:TieredStopAtLevel=1 (that will slow down the JIT later at the expense of the saved startup time).

The mentioned LazyInitBeanFactoryPostProcessor (you can use it for Spring 1.5 if you cannot apply flag spring.main.lazy-initialization=true available from Spring 2.2):

public class LazyInitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
      for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        definition.setLazyInit(true);
      }
  }
}

You can also use (or write your own - it's simple) something to analyse beans initialization time: https://github.com/lwaddicor/spring-startup-analysis

Hope it helps!

Share:
132,712

Related videos on Youtube

steady rain
Author by

steady rain

Updated on July 08, 2022

Comments

  • steady rain
    steady rain almost 2 years

    I have a Spring Boot application. I've added a lot of dependencies (unfortunately, looks I need all of them) and the startup time went up quite a lot. Just doing a SpringApplication.run(source, args) takes 10 seconds.

    While that might not be much compared to what are "used" to, I'm unhappy that it takes that much, mostly because it breaks the development flow. The application itself is rather small at this point, so I assume most of the time is related to the added dependencies, not the app classes themselves.

    I assume the issue is classpath scanning, but I am not sure how to:

    • Confirm that is the issue (i.e. how to "debug" Spring Boot)
    • If it really is the cause, how can I limit it, so it gets faster? For example, if I know that some dependency or package does not contain anything that Spring should be scanning, is there a way to limit that?

    I assume that enhancing Spring to have parallel bean initialization during startup would speed up things, but that enhancement request has been open since 2011, without any progress. I see some other efforts in Spring Boot itself, such as Investigate Tomcat JarScanning speed improvements, but that is Tomcat specific and has been abandoned.

    This article:

    although aimed at integration tests, suggests using lazy-init=true, however I do not know how to apply this to all beans in Spring Boot using Java configuration - any pointers here?

    Any (other) suggestions would be welcome.

    • M. Deinum
      M. Deinum over 9 years
      Post your code. Normally only the package the application runner is defined is is scanned. If you have other packages defined for @ComponentScan those are scanned as well. Another thing is to make sure you haven't enabled debug or trace logging as generally logging is slow, very slow.
    • Knut Forkalsrud
      Knut Forkalsrud about 9 years
      If you use Hibernate it also tends to eat significant time at application start.
    • Knut Forkalsrud
      Knut Forkalsrud about 9 years
      Spring's auto binding by type coupled with factory beans has the potential to be slow one you add a lot of beans and dependencies.
    • Cassian
      Cassian about 9 years
      Or you can use caching, spring.io/guides/gs/caching
    • steady rain
      steady rain about 9 years
      Thanks all for the comments - I would not be able to post the code unfortunately (a lot of internal jars), however I'm still looking for a way to debug this. Yes, I might be using A or B or doing X or Y, which slows it down. How do I determine this? If I add a dependency X, which has 15 transitive dependencies, how do I know which of those 16 slowed it down? If I can find out, is there anything I can do later to stop Spring from examining them? Pointers like that would be useful!
    • Kevin Wittek
      Kevin Wittek about 8 years
      Depending on your configured datasource, initializing your database schema takes a significant amount of time as well.
    • royalghost
      royalghost about 6 years
      Can you share your POM file ?
    • Dean Hiller
      Dean Hiller about 4 years
      Personally, I use webpieces in cloud run since bootup time 'without' hibernate is 1.5 seconds. We needed something fast plus it does the playframework style of never needing to reboot the server to do development. @steadyrain of course, I am biased.
  • idmitriev
    idmitriev almost 7 years
    did you measure this ??? Was it much faster?? In my opinion this is an exceptional case, much more important to make sure that Spring test context cache works
  • filpa
    filpa almost 6 years
    It does not seem that your project builds under gradle 4.8.1. Could you share which gradle version you used in your benchmarks?
  • Abhijit Sarkar
    Abhijit Sarkar almost 6 years
    @user991710 Based on my Gradle wrapper, I'm using v4.6. "Does not build" is a very vague statement, if you've something more specific, create a gist and post the link here. Your gist should list the steps you followed, and the error you're getting.
  • filpa
    filpa almost 6 years
    My apologies, I was a bit unclear. Checking out the project with a clean working directory and simply performing ./gradlew clean shadowJar as in the Readme results in the following error: > The value of a manifest attribute must not be null (Key=Start-Class).. I have made no local changes.
  • filpa
    filpa almost 6 years
    To add on to this, could you please add an example as to how someone might use your benchmark with a custom application? Does it have to be added as a project similar to minimal, or may the jar simply be supplied? I attempted to do the former but did not get very far.
  • Abhijit Sarkar
    Abhijit Sarkar almost 6 years
    @user991710 I thought that's obvious, but perhaps only to me. The way it currently works, the minimal-benchmark Gradle module directly includes the compiled output of the project being benchmarked (minimal). With some Gradle fiddling, and if you understand the idea behind creating the shadow jar, it should be possible to use the minimal-benchmark jar with your custom project, but I've not tried it myself. However, replacing the minimal module with your own works, and I use it in other projects. I don't have time to do a step-by-step walkthrough, but I hope the above helps.
  • loicmathieu
    loicmathieu about 5 years
    Don't run -Xverify:none on production as it breaks code verification and you can run into trouble. -XX:TieredStopAtLevel=1 is OK if you run an application for a small duration (a few seconds) otherwise it will be less productive as it will provide the JVM with long running optimizations.
  • apkisbossin
    apkisbossin about 5 years
    @idmitriev I just measured this on my application and my application started up at 53 seconds, compared to without exluding the autoconfiguration classes was 73 seconds. I did exclude many more classes than listed above though.
  • Bui Anh Tuan
    Bui Anh Tuan about 5 years
    For me, "There have been reports of hostname resolution being slow, but I didn't find it to be a problem for the apps I tested." => help me reduce ~10s. Cant believe it
  • Radu Toader
    Radu Toader about 5 years
    hibernate.hbm2ddl.auto=update has nothing to do with l2 cache. ddl..=update specifies to scan the current database schema, and compute the neccesary sql in order to update schema to reflect your entities. 'None' doesn't do this verification(also, doesn't try to update schema). Best practices is to use a tool like liquibase, where you will handle your schema changes,and you can also track them.
  • naXa stands with Ukraine
    naXa stands with Ukraine about 5 years
    @RaduToader this question and my answer are about speeding up Spring Boot startup time. They have nothing to do with Hibernate DDL vs Liquibase discussion; these tools both have their pros and cons. My point is that we can disable the DB schema update and enable only when necessary. Hibernate takes significant time on startup even when model did not change since the last run (for comparing DB schema with autogenerated schema). The same point is true for L2 cache.
  • Radu Toader
    Radu Toader about 5 years
    yes, i know that, but my point was that this is a bit dangerous to not explain what it truly does. You might very easily end up with your db empty.
  • naXa stands with Ukraine
    naXa stands with Ukraine about 5 years
    @RaduToader There was a link to a documentation page about DB initialization in my answer. Did you read it? It contains an exhaustive guide, listing all most popular tools (Hibernate and Liquibase, as well as JPA and Flyway). Also today I add a clear warning to the top of my answer. Do you think I need any other changes to explain consequences?
  • Radu Toader
    Radu Toader about 5 years
    Perfect. Thank you
  • Daniel
    Daniel about 5 years
    If database pooling is used and you are deploying on Linux, note that depleted system entropy can be a problem. By default Java (8 at least, and I believe most all versions) use /dev/random as an entropy source. Starting a database pool requires a good bit of entropy but /dev/random is blocking and can run out. Use -Djava.security.egd=file:/dev/urandom to switch to non-blocking (but arguably less random) entropy source. For our scenario (10 fat jars, all with database pools, starting concurrently) this was a 50-70% startup time reduction.
  • Abhijit Sarkar
    Abhijit Sarkar about 5 years
    @Daniel how is random number generation related to database pooling?
  • Daniel
    Daniel almost 5 years
    Many pools (Oracle UCP for sure, but in my testing also Hikari and Tomcat) encrypt data in the pool. I actually don't know if they are encrypting the connection info, or wrapping the stream. Regardless, encryption uses random number generation and so having a highly available, high throughput entropy source makes a noticeable difference in performance.
  • Abhijit Sarkar
    Abhijit Sarkar almost 5 years
    @Daniel In that case, there are other kind of pools too that you should consider. For example, Tomcat keeps both thread and connection pools for itself. Database connection pool may be a special case.
  • WirJun
    WirJun over 4 years
    as @loicmathieu said, -Xverify:none is bad, furthermore deprecated in JDK 13 and will likely be removed in a future release.
  • Abhijit Sarkar
    Abhijit Sarkar over 4 years
    @WirJun This answer doesn’t claim to be applicable until JDK 50; it was correct when it was written and still is. -Xverify:none exists such that people who are willing to accept the risk can do so; it’s their call, not yours or mine. My job here is to document the options available.
  • ArtOfWarfare
    ArtOfWarfare over 4 years
    The question is about improving boot time, not compile time.
  • naXa stands with Ukraine
    naXa stands with Ukraine over 4 years
    @ArtOfWarfare read the question again. the question states the problem as "I'm unhappy that it takes that much [time], mostly because it breaks the development flow". I felt like this is a primary problem and addressed it in my answer.
  • Isuru Dewasurendra
    Isuru Dewasurendra over 4 years
    if you enable lazy initialization, first time loading is super fast, but when client accessing for the first time it may notice some delay. I really recommend this for the development not for the production.
  • user1767316
    user1767316 over 4 years
    How to mesure Spring Boot startup time of a unit test run from intellij ?
  • Abhijit Sarkar
    Abhijit Sarkar over 4 years
    @user1767316 open a new question
  • user1767316
    user1767316 over 4 years
    How to deal with Private configuration classes ?
  • Narendra Jaggi
    Narendra Jaggi about 4 years
    As @IsuruDewasurendra suggested, it is rightly not a recommended way, it can significantly increase the latency when the app starts serving load.
  • Abhijit Sarkar
    Abhijit Sarkar over 3 years
    It just kicks the can down the road.
  • Caio
    Caio over 3 years
    I use Lazy Initialization only on development because the first access is so lazy, but it is good feature in Spring Boot.
  • Donal Fellows
    Donal Fellows almost 3 years
    Re host name resolution, that's very OS-specific. In particular, resolving the current machine name on macOS is extremely slow. See apple.stackexchange.com/questions/175320/…
  • payne
    payne over 2 years
    Is there a way to automatically know which auto-configurations are actually used? Maybe some long-running thing that adds up all auto-configs used throughout the app life-time and then you can poll an actuator endpoint to see that list?
  • luboskrnac
    luboskrnac over 2 years
    @payne, I am not aware of anything like you are describing.