记一次 SpringBoot 启动优化实践
背景
最近看到一本关于 Java 应用通过基础设施于工具的改进,实现从构建到启动全方面提速的小册。想着自己目前在做的项目最让我觉得不爽的就是项目启动。尤其是需要频繁改代码,频繁重启项目的时候,每次项目启动起码都要两分钟才能启动完毕,挺影响开发效率的。就想着结合看的小册来做一个项目的启动优化,至少让自己在开发中更流畅
可能有人会说,可以使用devTools
来做热部署。对这玩意没有太了解就没有用
项目以及各项参数
SpringBoot 版本:1.5.6
机器内存:16G
JVM 参数:默认值
AspectJ 版本:1.8.8
启动整体耗时:150s~160s
优化结果
优化前
优化后
耗时排查
前提:我对 SpringBoot 以及 Spring 的理解还局限在八股文,
思路 1(笨方法) :所以对于排查的思路不说很聪明很有用,但至少很容易看懂。就是每一条日志看呗,如果明显的慢,在打印日志的时候肯定会停顿的,看一下接下来打印的日志是什么内容。排查的思路当然就是在上一句日志和打印出来的这句日志中找问题了。
思路 2:SpringBoot 的启动过程,肯定会加载很多 Bean,并且这些 Bean 都会走一遍 Bean 的生命周期。只要在加载每个 Bean 的过程中,记录每个 Bean 的初始化耗时,就可以大致知道问题出在哪里。可以使用 BeanPostProcessor
接口来监控 Bean 的注入耗时。BeanPostProcessor
是 Spring 提供的 Bean 初始化前后的钩子函数,用于执行一切自定义逻辑。下面是借鉴掘金一位大佬文章中的代码。
如果项目中的 Bean 数量不是特别多,我觉得可以换成 Stopwatch 来进行计时会更优雅一些!
思路 3: 遇事不决加内存!
优化方案
对于思路 1,就不多说了。其实没有那么好用。要不然我也不会去翻一开始说的那本书。
方案 1:SpringBoot 实现 Bean 的懒加载
SpringBoot 实现这一功能的版本是:2.2。不符合项目要求
方案 2:调整 JVM 参数实现启动优化
这个方案只是我个人认为是可行的,但其实没有什么用。主要是我理解的误区,我以为堆内存设置的足够大,减少 GC 频率,就可以提高启动速度。
有这种想法的原因是因为我在个人项目中使用过,因为这个项目刚起步,写的内容没那么多,随便设置几个 JVM 参数,堆内存设置大点,启动速度也就两三秒,不设置 JVM 参数,启动速度大概4~6
秒。
而我在项目中使用,因为是本地,JVM 参数并没有设置过,堆内存最小就是电脑内存的 1/64,最大就是内存的 1/4。结果堆内存设置大一点,其实启动速度没什么变化。
方案 3(《Java 应用提速》中提供):升级 AspectJ 版本
这种方案的前提是AspectJ
的版本要低于1.9.0
,并且如果应用中有使用到通过注解(RetentionPolicy 需要是 RUNTIME)来做切面表达式
我看了书中的方案,就去翻了一下项目中关于AspectJ
依赖的版本1.8.8
,刚好在1.9.0
下,并且翻了一下项目中所有做切面表达式的注解的生命周期都是RUNTIME
的。
于是,我就试着修改 AspectJ
版本,结果真的如书中所说,从 150 秒 降到了 70 多秒,提速了一半。
原理
在老版本的aspectJ
在判断一个bean
的 method 上是否有 annotation
的方法,发现它是去 jar 包里读取 Class 文件并解析类信息,耗时在类搜索和解析上。
unpackAnnotations()
annotaionFindler.getAnnotations()
而 Method 类本身就有getAnnotation
方法,不需要自己加载类信息和解析
AspectJ 去 class 源文件中读取的原因是因为annotaion
的生命周期如果不是RUNTIME
的话,运行时是获取不到的
在 1.9.0 以后的版本中,已经解决该问题。
新版本在判断是否有注解的逻辑:与老版本的差异在于会判断 annotaion
的生命周期是不是RUNTIME
的,是的话,就直接从 Method 中获取了,不是才会加载源文件解析
其实应该还是可以继续优化的,后面再找找有没有别的思路。
作者:不知名玲玲
链接:https://juejin.cn/post/7184428718465482810
来源:稀土掘金
评论