Java 加快 Spring Boot 启动时间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27230702/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Speed up Spring Boot startup time
提问by steady rain
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.
我有一个 Spring Boot 应用程序。我添加了很多依赖项(不幸的是,看起来我需要所有依赖项)并且启动时间增加了很多。只做一个SpringApplication.run(source, args)
需要10秒钟。
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?
- 确认是问题所在(即如何“调试”Spring Boot)
- 如果真的是原因,我该如何限制它,让它变得更快?例如,如果我知道某些依赖项或包不包含 Spring 应该扫描的任何内容,有没有办法限制它?
I assume that enhancing Spring to have parallel bean initialization during startupwould 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.
我认为在启动期间增强 Spring 以具有并行 bean 初始化会加快速度,但该增强请求自 2011 年以来一直开放,没有任何进展。我在 Spring Boot 本身中看到了其他一些努力,例如Investigate Tomcat JarScanning speed Improvements,但那是 Tomcat 特定的,已被放弃。
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?
尽管针对集成测试,建议使用lazy-init=true
,但是我不知道如何使用 Java 配置将其应用于 Spring Boot 中的所有 bean - 这里有任何指针吗?
Any (other) suggestions would be welcome.
欢迎任何(其他)建议。
回答by steady rain
To me it sounds like you're using a wrong configuration setting. Start by checking myContainer and possible conflicts. To determine who is using the most resources you have to check the memory maps (see the amount of data!) for each dependency at a time - and that takes plenty of time, as well... (and SUDO privileges). By the way: are you usually testing the code against the dependencies?
对我来说,这听起来像是您使用了错误的配置设置。首先检查 myContainer 和可能的冲突。要确定谁使用的资源最多,您必须一次检查每个依赖项的内存映射(查看数据量!) - 这也需要大量时间......(以及 SUDO 权限)。顺便说一句:您通常是否针对依赖项测试代码?
回答by luboskrnac
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
Spring Boot 做了很多可能不需要的自动配置。因此,您可能只想缩小应用程序所需的自动配置范围。要查看包含的自动配置的完整列表,只需org.springframework.boot.autoconfigure
在调试模式 ( logging.level.org.springframework.boot.autoconfigure=DEBUG
in application.properties
)下运行日志记录。另一种选择是使用以下--debug
选项运行 spring boot 应用程序: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,
HymansonAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
ThymeleafAutoConfiguration.class,
WebMvcAutoConfiguration.class,
WebSocketAutoConfiguration.class,
})
public class SampleWebUiApplication {
Code was copied from this blog post.
回答by pczeus
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.
您可以看到幕后发生的事情并启用调试日志记录,就像在从命令行启动应用程序时指定 --debug 一样简单。您还可以在 application.properties 中指定 debug=true。
Also, you can set the logging level in application.properties as simple as:
此外,您可以在 application.properties 中设置日志记录级别,如下所示:
logging.level.org.springframework.web: DEBUG logging.level.org.hibernate: ERROR
logging.level.org.springframework.web:调试 logging.level.org.hibernate:错误
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
如果您检测到不需要的自动配置模块,则可以将其禁用。可以在此处找到此文档: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 {
}
回答by Abhijit Sarkar
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 11226contains a lot of useful information. There is also a ticket 7939open to adding timing information to condition evaluation, but it doesn't seem to have a specific ETA.
迄今为止投票最多的答案并没有错,但它没有深入到我喜欢看到的深度,也没有提供任何科学证据。Spring Boot 团队进行了一次缩短 Boot 2.0 启动时间的练习,票证11226包含了很多有用的信息。还有一张票7939可以将时间信息添加到条件评估中,但它似乎没有特定的 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
调试引导启动的最有用、最有条理的方法是由 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-benchmarkproject. 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.
我也有一个类似的用例,所以我采用了 Dave 的 JMH 微基准测试方法并运行了它。结果是引导基准项目。我将它设计为可用于测量任何 Spring Boot 应用程序的启动时间,使用由bootJar
(以前bootRepackage
在 Boot 1.5 中调用)Gradle 任务生成的可执行 jar 。随意使用它并提供反馈。
My findings are as follows:
我的发现如下:
- CPU matters. A lot.
- Starting the JVM with -Xverify:nonehelps significantly.
- Excluding unnecessary autoconfigurations helps.
- 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. - There have been reportsof hostname resolution being slow, but I didn't find it to be a problem for the apps I tested.
- CPU 很重要。很多。
- 使用-Xverify:none启动 JVM 有很大帮助。
- 排除不必要的自动配置会有所帮助。
- Dave 推荐了 JVM 参数-XX:TieredStopAtLevel=1,但我的测试没有显示出显着的改进。此外,
-XX:TieredStopAtLevel=1
可能会减慢您的第一个请求。 - 有报告称主机名解析很慢,但我没有发现这对我测试的应用程序来说是个问题。
回答by nobar
If you're trying to optimize development turn-around for manual testing, I strongly recommend the use of devtools.
如果您正在尝试优化手动测试的开发周转,我强烈建议使用devtools。
Applications that use spring-boot-devtools will automatically restart whenever files on the classpath change.
每当 classpath 上的文件发生更改时,使用 spring-boot-devtools 的应用程序将自动重新启动。
Just recompile -- and the server will restart itself (for Groovy you only need to update the source file). if you're using an IDE (e.g. 'vscode'), it may automatically compile your java files, so just saving a java file can initiate a server restart, indirectly -- and Java becomes just as seamless as Groovy in this regard.
只需重新编译——服务器将自行重新启动(对于 Groovy,您只需要更新源文件)。如果您使用的是 IDE(例如“vscode”),它可能会自动编译您的 java 文件,因此只需保存一个 java 文件就可以间接启动服务器重新启动——在这方面,Java 变得与 Groovy 一样无缝。
The beauty of this approach is that the incremental restart short-circuits some of the from-scratch startup steps -- so your service will be back up and running much more quickly!
这种方法的美妙之处在于增量重启缩短了一些从头开始的启动步骤——因此您的服务将更快地备份和运行!
Unfortunately, this doesn't help with startup times for deployment or automated unit testing.
不幸的是,这对部署或自动化单元测试的启动时间没有帮助。
回答by naXa
I find it strange nobody suggested these optimizations before. Here're some general tips on optimizing project build and startup when developing:
我觉得很奇怪以前没有人建议过这些优化。以下是一些在开发时优化项目构建和启动的一般提示:
- exclude development directories from antivirus scanner:
- project directory
- build output directory (if it's outside of project directory)
- IDE indices directory (e.g. ~/.IntelliJIdea2018.3)
- deployment directory (webapps in Tomcat)
- upgrade hardware. use faster CPU and RAM, better internet connection (for downloading dependencies) and database connection, switch to SSD. a video card doesn't matter.
- 从防病毒扫描程序中排除开发目录:
- 项目目录
- 构建输出目录(如果它在项目目录之外)
- IDE 索引目录(例如 ~/.IntelliJIdea2018.3)
- 部署目录(Tomcat中的webapps)
- 升级硬件。使用更快的 CPU 和 RAM、更好的互联网连接(用于下载依赖项)和数据库连接,切换到 SSD。显卡无所谓。
WARNINGS
警告
- the first option comes for the price of reduced security.
- the second option costs money (obviously).
- 第一种选择是以降低安全性为代价的。
- 第二种选择需要花钱(显然)。
回答by naXa
WARNING:If you don't use Hibernate DDL for automatic DB schema generation and you don't use L2 cache, this answer is NOT applicable to you. Scroll ahead.
警告:如果您不使用 Hibernate DDL 自动生成 DB 模式并且不使用 L2 缓存,则此答案不适用于您。向前滚动。
My finding is that Hibernate adds significant time to application startup. Disabling L2 cache and database initializationresults in faster Spring Boot app startup. Leave cache ON for production and disable it for your development environment.
我的发现是 Hibernate 为应用程序启动增加了大量时间。禁用 L2 缓存和数据库初始化会导致 Spring Boot 应用程序启动更快。为生产保留缓存,并为您的开发环境禁用它。
application.yml:
应用程序.yml:
spring:
jpa:
generate-ddl: false
hibernate:
ddl-auto: none
properties:
hibernate:
cache:
use_second_level_cache: false
use_query_cache: false
Test results:
检测结果:
L2 cache is ON and
ddl-auto: update
INFO 5024 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 23331 ms INFO 5024 --- [restartedMain] b.n.spring.Application : Started Application in 54.251 seconds (JVM running for 63.766)
L2 cache is OFF and
ddl-auto: none
INFO 10288 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 9863 ms INFO 10288 --- [restartedMain] b.n.spring.Application : Started Application in 32.058 seconds (JVM running for 37.625)
L2 缓存开启且
ddl-auto: update
INFO 5024 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 23331 ms INFO 5024 --- [restartedMain] b.n.spring.Application : Started Application in 54.251 seconds (JVM running for 63.766)
L2 缓存关闭且
ddl-auto: none
INFO 10288 --- [restartedMain] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 9863 ms INFO 10288 --- [restartedMain] b.n.spring.Application : Started Application in 32.058 seconds (JVM running for 37.625)
Now I wonder what will I do with all this free time
现在我想知道这些空闲时间我会做什么
回答by Niraj Sonawane
Spring Boot 2.2.M1has added feature to support Lazy Initialization in Spring Boot.
Spring Boot 2.2.M1添加了支持 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.
默认情况下,刷新应用程序上下文时,会创建上下文中的每个 bean 并注入其依赖项。相比之下,当 bean 定义被配置为延迟初始化时,它不会被创建,并且在需要之前不会注入它的依赖项。
Enabling Lazy InitializationSet spring.main.lazy-initialization
to true
启用延迟初始化设置spring.main.lazy-initialization
为true
When to Enable Lazy Initializationlazy 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
有关更多详细信息,请查看文档
回答by Przemek Nowak
Well there is entire list of possible actions described here: https://spring.io/blog/2018/12/12/how-fast-is-spring
好吧,这里描述了完整的可能操作列表: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):
我将把 Spring 方面最重要的笔记(稍微调整一下):
- Classpath exclusions from Spring Boot web starters:
- Hibernate Validator
- Hymanson (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 (useLazyInitBeanFactoryPostProcessor
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).
- Spring Boot web starters 中的类路径排除:
- 休眠验证器
- Hymanson(但 Spring Boot 执行器依赖于它)。如果您需要 JSON 渲染,请使用 Gson(仅适用于开箱即用的 MVC)。
- Logback:改用 slf4j-jdk14
- 使用弹簧上下文索引器。它不会增加太多,但每一点都有帮助。
- 如果您负担不起,请不要使用执行器。
- 使用 Spring Boot 2.1 和 Spring 5.1。可用时切换到 2.2 和 5.2。
- 使用
spring.config.location
(命令行参数或系统属性等)修复 Spring Boot 配置文件的位置。在 IDE 中进行测试的示例:spring.config.location=file://./src/main/resources/application.properties
. - 如果不需要,请关闭 JMX
spring.jmx.enabled=false
(这是 Spring Boot 2.2 中的默认设置) - 默认情况下使 bean 定义为惰性。
spring.main.lazy-initialization=true
Spring Boot 2.2 中有一个新标志(LazyInitBeanFactoryPostProcessor
用于旧的 Spring)。 - 解压胖 jar 并使用显式类路径运行。
- 使用
-noverify
. 还要考虑-XX:TieredStopAtLevel=1
(这将在稍后以节省的启动时间为代价减慢 JIT)。
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):
提到的LazyInitBeanFactoryPostProcessor
(如果您不能应用spring.main.lazy-initialization=true
Spring 2.2 提供的标志,您可以将它用于 Spring 1.5 ):
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
您还可以使用(或编写自己的 - 这很简单)一些东西来分析 bean 初始化时间:https: //github.com/lwaddicor/spring-startup-analysis
Hope it helps!
希望能帮助到你!
回答by Daulet Kadirbekov
In my case, there was too much breakpoints. When I clicked "Mute Breakpoints" and restarted application in debug mode, application started in 10 times faster.
就我而言,断点太多。当我单击“静音断点”并在调试模式下重新启动应用程序时,应用程序的启动速度提高了 10 倍。