SpringBoot应用启动优化

1、背景

alibee-shop是我所在团队的核心应用,迭代了8年之久。经过不断地功能迭代,这个应用的启动时间越来越长。截止到24年3月份,启动时间就达到了900s以上,也就是需要15分钟以上才能完成启动。很明显,这样的启动时间严重的影响开发效率,同时也为稳定性带了巨大隐患。光紧急回滚就要花15分钟,无法做到故障的快速恢复。这个启动的问题是项目组的一个顽疾,经过了多位开发的多次优化仍没有显著效果。本文会介绍一下自己的优化方案,如何将这个应用的启动时间缩短至6分钟左右,也就是缩减了将近2/3。

2、结果

先看下优化结果。优化方案在预发的试验效果显著,启动时间可以维持在400s左右,也就是6分钟左右。

3、启动耗时分析

这里的文档呢,是部门内总结的应用启动优化方案。这个文档的优化思路主要有两个,第一个是通过添加JVM启动参数,优化依赖的框架或者中间件的配置,达到缩短框架/中间件初始化耗时的目的。第二个是考虑将Spring容器初始化Bean的方式从同步改成异步,以减少Bean初始化的耗时。以上都是比较通用的方案。我实践后发现第一种方案对于我负责的应用还是有效果的,可以将启动缩短两分钟左右,但是第二种方案无明显效果。这让我有些疑惑,我之前的认知一直以为启动慢是因为Spring容器中的Bean太多了,一定是初始化Bean耗时太高,导致应用启动慢的。异步初始化一定是有效果的,但是很遗憾,现实情况并不是这样。经过统计呢,发现Spring容器中共1.7k个bean,初始化耗时也就大概4分钟左右。既然不是Bean的初始化导致耗时高,那问题究竟在哪呢?分析到这里优化就进入了死胡同,没有了新的思路。

4、柳暗花明

常言道,能发现问题的关键,就解决了问题的一半。所以,基于现象能分析出问题的核心所在是非常关键的。方向错了是最可怕的。既然启动慢的根本问题不是Bean的初始化,再怎么异步处理收益也不大。怎么能找到根本的问题呢?

鹰眼热点方法分析

在java的性能分析领域还是很多工具可以使用的。首先我想到的是鹰眼的热点分析功能,它能帮助你定位是什么热点方法占用了大量的资源。如果我对应用启动过程进行诊断,就能定位到热点方法,也就定位到了问题的关键。但是遗憾的是我在鹰眼上采集了很多次,都没看到诊断结果。坑爹是页面一直提示我“1分钟后右侧历史记录会显示结果”,我一度以为是我的应用接入应用的方式有问题。苦恼了一天之后,我找到了鹰眼的值班同学,排查后他告诉我,鹰眼不支持预发诊断。走到这里发现此路不通。

halo-profiler

当一筹莫展时,猛然回首发现之前的优化文档中提到这个工具。这个工具基于java agent机制,可以attach到应用进程上,对进程的启动过程进行采样,然后进行热点方法分析。这下算是找到救星了。但是按文档操作一番后,并未生成诊断日志。经过与作者的友好沟通后,作者表示。

咨询作者后发现工具不支持我的应用。

经过作者耐心指导和配置后,工具直接挂了。

后来使用了工具的手动模式,顺利完成启动过程的采集。在这里还是非常感谢作者的耐心指导。工具链接放在这里星环启动耗时分析工具:halo-profiler

采集到热点方法后,我惊奇的发现最耗时操作居然是类加载。

$head -n 100 idx.log
index|samplingRate|className|methodName|recordCount|samplingCount|invokeCount|totalUseTime|avgPerInvokeUseTime|avgPerInvokeUseTimeColor|invokes(address_rate)
0000|100.00%|Total|--|877|16633|16633|876698ms|53ms|0|
0001|100.00%|com.alibee.shop.web.Application|main|877|16633|1|876751ms|876751ms|R|0008_97.3%,0200_2.7%
0002|100.00%|java.lang.reflect.Method|invoke|877|16633|1|876751ms|876751ms|R|0006_100.0%
0003|100.00%|java.lang.Thread|run|877|16633|1|876751ms|876751ms|R|0007_100.0%
0004|100.00%|sun.reflect.NativeMethodAccessorImpl|invoke0|877|16633|1|876751ms|876751ms|R|0001_100.0%
0005|100.00%|sun.reflect.NativeMethodAccessorImpl|invoke|877|16633|1|876751ms|876751ms|R|0004_100.0%
0006|100.00%|sun.reflect.DelegatingMethodAccessorImpl|invoke|877|16633|1|876751ms|876751ms|R|0005_100.0%
0007|100.00%|com.taobao.pandora.boot.loader.LaunchRunner|run|877|16633|1|876751ms|876751ms|R|0002_100.0%
0008| 97.28%|org.springframework.boot.SpringApplication|run|854|16181|1|852925ms|852925ms|R|0009_95.6%,0154_4.3%,0223_0.1%,2881_0.0%,4935_0.0%,5353_0.0%,5155_0.0%
0009| 94.60%|org.springframework.boot.SpringApplication|refreshContext|831|15735|2|829416ms|414708ms|R|0010_100.0%
0010| 94.60%|org.springframework.boot.SpringApplication|refresh|831|15735|2|829416ms|414708ms|R|0012_98.3%,0011_1.7%
0011| 94.60%|org.springframework.context.support.AbstractApplicationContext|refresh|831|15735|2|829416ms|414708ms|R|0047_49.4%,0060_26.5%,0070_20.2%,0173_3.4%,0645_0.2%,1092_0.1%,2149_0.0%,3145_0.0%,3044_0.0%,0858_0.0%
0012| 93.03%|org.springframework.boot.context.embedded.EmbeddedWebApplicationContext|refresh|818|15473|1|815605ms|815605ms|R|0011_100.0%
0013| 90.85%|java.security.AccessController|doPrivileged|801|15111|6286|796524ms|127ms|B|0029_43.0%,0064_24.9%,0096_9.0%,0081_8.8%,0149_4.6%,0136_2.7%,0251_1.6%,0137_1.6%,0378_0.9%,0470_0.6%,0300_0.5%,0243_0.4%,0650_0.4%,0462_0.3%,0346_0.1%,0613_0.1%,1105_0.1%,0620_0.1%,2215_0.0%,1850_0.0%,2003_0.0%,3380_0.0%,4464_0.0%,5598_0.0%,4679_0.0%,2017_0.0%,1130_0.0%
0014| 80.06%|org.springframework.beans.factory.support.AbstractBeanFactory|doGetBean|704|13316|20|701907ms|35095ms|R|0018_99.9%,0067_0.1%,0020_0.0%
0015| 80.06%|org.springframework.beans.factory.support.AbstractBeanFactory|getBean|704|13316|20|701907ms|35095ms|R|0014_100.0%
0016| 80.05%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|createBean|704|13315|20|701854ms|35093ms|R|0017_99.0%,0422_1.0%,0244_0.0%
0017| 80.04%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|doCreateBean|704|13313|24|701748ms|29240ms|R|0035_45.6%,0059_32.0%,0051_21.9%,0511_0.5%
0018| 80.00%|org.springframework.beans.factory.support.DefaultSingletonBeanRegistry|getSingleton|704|13307|20|701432ms|35072ms|R|0019_100.0%,1174_0.0%
0019| 80.00%|org.springframework.beans.factory.support.AbstractBeanFactory$1|getObject|704|13306|19|701379ms|36915ms|R|0016_100.0%
0020| 70.50%|java.lang.ClassLoader|loadClass|627|11726|9418|618095ms|66ms|C|0021_89.9%,0100_10.0%,3246_0.0%,4624_0.0%
0021| 69.54%|com.taobao.pandora.boot.loader.ReLaunchURLClassLoader|loadClass|620|11566|9362|609661ms|65ms|C|0023_99.6%,0929_0.3%,0708_0.1%,5204_0.0%
0022| 69.32%|org.springframework.boot.loader.jar.JarURLConnection|getInputStream|612|11530|10387|607764ms|59ms|C|0028_98.5%,0779_0.4%
0023| 69.28%|com.taobao.pandora.boot.loader.ReLaunchURLClassLoader|doLoadClass|619|11524|9328|607448ms|65ms|C|0024_58.2%,0020_39.9%,0289_1.9%
0024| 68.45%|java.net.URLClassLoader|findClass|611|11385|9943|600121ms|60ms|C|0013_99.8%,1111_0.2%
0025| 68.40%|java.lang.Throwable|<init>|605|11377|10288|599699ms|58ms|C|0026_100.0%
0026| 68.40%|java.lang.Throwable|fillInStackTrace|605|11377|10288|599699ms|58ms|C|
0027| 68.40%|java.lang.Exception|<init>|605|11377|10288|599699ms|58ms|C|0025_100.0%
0028| 68.28%|org.springframework.boot.loader.jar.JarURLConnection|connect|604|11357|10267|598645ms|58ms|C|0030_99.9%
0029| 68.24%|java.net.URLClassLoader$1|run|608|11351|9911|598328ms|60ms|C|0033_94.3%,0159_5.7%
0030| 68.20%|org.springframework.boot.loader.jar.JarURLConnection|throwFileNotFound|602|11344|10255|597959ms|58ms|C|0032_99.9%,1610_0.0%,3455_0.0%
0031| 68.16%|java.io.IOException|<init>|601|11337|10248|597591ms|58ms|C|0027_100.0%
0032| 68.15%|java.io.FileNotFoundException|<init>|601|11336|10247|597538ms|58ms|C|0031_100.0%
0033| 65.89%|sun.misc.URLClassPath|getResource|590|10959|9922|577666ms|58ms|C|0034_99.5%,0777_0.3%,1318_0.1%,3949_0.0%
0034| 65.56%|sun.misc.URLClassPath$Loader|getResource|586|10905|9880|574819ms|58ms|C|0022_99.4%,0571_0.3%,0522_0.3%,3407_0.0%
0035| 63.55%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|populateBean|569|10571|334|557213ms|1668ms|Y|0052_57.9%,0046_30.4%,0120_4.4%,0172_3.0%,0194_3.0%,0180_1.1%,1024_0.2%
0036| 53.73%|java.lang.Class|forName|485|8937|7960|471083ms|59ms|C|0037_100.0%
0037| 53.73%|java.lang.Class|forName0|485|8937|7960|471083ms|59ms|C|0020_98.7%,0479_0.1%,1930_0.1%,2304_0.0%,2283_0.0%,4785_0.0%,4990_0.0%,3918_0.0%,3934_0.0%,5244_0.0%,4336_0.0%,5094_0.0%,4066_0.0%,4486_0.0%,3675_0.0%,5248_0.0%,5393_0.0%,1111_0.0%
0038| 52.77%|java.beans.Introspector|getBeanInfo|480|8777|1070|462649ms|432ms|B|0054_62.5%,0072_36.7%,0590_0.8%,3825_0.0%
0039| 52.29%|com.sun.beans.finder.ClassFinder|findClass|478|8697|5038|458432ms|91ms|C|0036_100.0%
0040| 52.07%|org.springframework.beans.CachedIntrospectionResults|forClass|473|8661|1037|456534ms|440ms|B|0041_100.0%,3939_0.0%
0041| 52.07%|org.springframework.beans.CachedIntrospectionResults|<init>|472|8660|1037|456482ms|440ms|B|0038_99.4%,0721_0.6%,1994_0.1%,1478_0.0%
0042| 51.11%|org.springframework.beans.BeanWrapperImpl|getCachedIntrospectionResults|463|8501|1003|448101ms|447ms|B|0040_100.0%
0043| 50.71%|org.springframework.beans.BeanWrapperImpl|getPropertyDescriptors|459|8435|989|444622ms|450ms|B|0042_100.0%
0044| 49.59%|org.springframework.beans.factory.support.DefaultListableBeanFactory|doResolveDependency|446|8249|93|434817ms|4675ms|Y|0049_78.8%,0062_18.1%,0079_3.1%,4509_0.0%,3676_0.0%
0045| 48.72%|org.springframework.beans.factory.support.DefaultListableBeanFactory|resolveDependency|437|8103|91|427121ms|4694ms|Y|0044_99.9%,2131_0.1%,0020_0.0%,2359_0.0%
0046| 47.89%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|filterPropertyDescriptorsForDependencyCheck|433|7966|931|419900ms|451ms|B|0043_100.0%,4386_0.0%
0047| 46.75%|org.springframework.context.support.AbstractApplicationContext|finishBeanFactoryInitialization|420|7776|2|409885ms|204942ms|R|0048_100.0%
0048| 46.75%|org.springframework.beans.factory.support.DefaultListableBeanFactory|preInstantiateSingletons|420|7776|2|409885ms|204942ms|R|0015_91.1%,0013_8.9%,0020_0.0%
0049| 40.53%|org.springframework.beans.factory.config.DependencyDescriptor|resolveCandidate|367|6742|83|355381ms|4282ms|Y|0015_100.0%
0050| 39.57%|org.springframework.beans.factory.annotation.InjectionMetadata|inject|358|6581|76|346895ms|4564ms|Y|0053_88.0%,0116_7.0%,0225_5.0%
0051| 39.48%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|createBeanInstance|343|6567|163|346157ms|2124ms|Y|0057_81.2%,0094_17.2%,0464_1.6%,0244_0.0%,2828_0.0%
0052| 39.10%|org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor|postProcessPropertyValues|355|6504|72|342836ms|4762ms|Y|0050_100.0%
0053| 37.20%|org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement|inject|338|6188|74|326179ms|4408ms|Y|0045_100.0%,0020_0.0%
0054| 36.36%|java.beans.Introspector|getTargetBeanDescriptor|341|6047|3183|318747ms|100ms|B|0055_100.0%
0055| 36.36%|java.beans.Introspector|beanDescriptorOf|341|6047|3183|318747ms|100ms|B|0056_100.0%
0056| 36.36%|java.beans.Introspector|findCustomizerClass|341|6047|3183|318747ms|100ms|B|0039_100.0%,2412_0.0%
0057| 32.21%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|instantiateUsingFactoryMethod|273|5358|129|282428ms|2189ms|Y|0058_100.0%
0058| 32.21%|org.springframework.beans.factory.support.ConstructorResolver|instantiateUsingFactoryMethod|273|5358|129|282428ms|2189ms|Y|0015_36.1%,0013_33.9%,0074_29.7%,0758_0.2%,4967_0.0%
0059| 31.70%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|initializeBean|260|5273|316|277948ms|880ms|B|0013_71.4%,0114_11.2%,0166_8.8%,0221_5.7%,0317_2.8%
0060| 25.30%|org.springframework.context.support.AbstractApplicationContext|registerBeanPostProcessors|218|4208|4|221810ms|55453ms|R|0061_100.0%
0061| 25.30%|org.springframework.context.support.PostProcessorRegistrationDelegate|registerBeanPostProcessors|218|4208|4|221810ms|55453ms|R|0015_100.0%,0020_0.0%,0085_0.0%
0062| 24.02%|org.springframework.beans.factory.support.DefaultListableBeanFactory|findAutowireCandidates|218|3996|136|210635ms|1549ms|Y|0080_60.5%,0086_39.4%,2465_0.1%,1112_0.0%
0063| 22.65%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|access$000|193|3767|6|198564ms|33094ms|R|0065_100.0%
0064| 22.65%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$5|run|193|3767|6|198564ms|33094ms|R|0063_100.0%
0065| 22.65%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|invokeAwareMethods|193|3767|6|198564ms|33094ms|R|0066_99.7%,2031_0.1%,1349_0.1%,5487_0.0%
0066| 22.59%|com.alibaba.intl.arch.bean.async.core.processor.FirstLoadBeanPostProcessor|setBeanFactory|193|3757|1|198037ms|198037ms|R|0015_100.0%
0067| 21.05%|org.springframework.cloud.context.scope.GenericScope|get|179|3502|3|184596ms|61532ms|R|0069_100.0%
0068| 21.05%|org.springframework.beans.factory.support.AbstractBeanFactory$2|getObject|179|3502|3|184596ms|61532ms|R|0016_100.0%
0069| 21.05%|org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper|getBean|179|3502|3|184596ms|61532ms|R|0068_100.0%
0070| 19.76%|org.springframework.context.support.AbstractApplicationContext|invokeBeanFactoryPostProcessors|169|3287|4|173263ms|43316ms|R|0071_100.0%,0020_0.0%
0071| 19.76%|org.springframework.context.support.PostProcessorRegistrationDelegate|invokeBeanFactoryPostProcessors|169|3286|4|173210ms|43303ms|R|0015_49.0%,0109_32.0%,0283_7.3%,0318_5.8%,0488_3.2%,0773_1.3%,0085_0.6%,1291_0.5%,1891_0.2%,2506_0.1%,5246_0.0%,4475_0.0%,4803_0.0%,3955_0.0%,5233_0.0%
0072| 19.37%|java.beans.Introspector|<init>|165|3222|1630|169837ms|104ms|B|0076_69.3%,0038_30.7%,1478_0.0%
0073| 16.20%|org.springframework.beans.factory.support.ConstructorResolver|resolveAutowiredArgument|146|2695|29|142058ms|4899ms|Y|0045_100.0%
0074| 16.20%|org.springframework.beans.factory.support.ConstructorResolver|createArgumentArray|146|2695|29|142058ms|4899ms|Y|0073_100.0%
0075| 15.95%|com.sun.beans.finder.InstanceFinder|find|137|2653|1855|139844ms|75ms|C|0077_100.0%
0076| 15.95%|java.beans.Introspector|findExplicitBeanInfo|137|2653|1855|139844ms|75ms|C|0075_100.0%
0077| 15.95%|com.sun.beans.finder.InstanceFinder|instantiate|137|2653|1855|139844ms|75ms|C|0039_100.0%,2412_0.0%
0078| 14.77%|org.springframework.beans.factory.support.SimpleInstantiationStrategy|instantiate|124|2456|157|129459ms|825ms|B|0002_94.0%,0286_5.9%,0013_0.1%,0020_0.0%
0079| 14.61%|org.springframework.beans.factory.support.DefaultListableBeanFactory|resolveMultipleBeans|134|2430|23|128089ms|5569ms|Y|0062_99.8%,2235_0.2%,4046_0.0%
0080| 14.53%|org.springframework.beans.factory.support.DefaultListableBeanFactory|addCandidateEntry|133|2417|25|127404ms|5096ms|Y|0049_100.0%,3402_0.0%
0081| 13.91%|org.springframework.beans.factory.support.ConstructorResolver$3|run|116|2313|126|121922ms|968ms|B|0078_100.0%
0082| 12.22%|sun.misc.Unsafe|park|103|2032|84|107110ms|1275ms|Y|0083_100.0%
0083| 12.22%|sun.misc.Unsafe|park0|103|2032|84|107110ms|1275ms|Y|
0084| 11.24%|org.springframework.beans.factory.support.DefaultListableBeanFactory|doGetBeanNamesForType|97|1869|187|98518ms|527ms|B|0087_83.4%,0224_15.8%,3414_0.1%
0085| 11.24%|org.springframework.beans.factory.support.DefaultListableBeanFactory|getBeanNamesForType|97|1869|187|98518ms|527ms|B|0084_100.0%
0086|  9.79%|org.springframework.beans.factory.BeanFactoryUtils|beanNamesForTypeIncludingAncestors|86|1629|170|85867ms|505ms|B|0085_99.9%
0087|  9.37%|org.springframework.beans.factory.support.AbstractBeanFactory|isTypeMatch|80|1558|93|82125ms|883ms|B|0089_94.0%,0220_3.0%,0092_1.9%,1112_0.2%,2409_0.2%,2841_0.1%,0018_0.1%,4863_0.1%
0088|  9.08%|java.util.concurrent.CountDownLatch|await|76|1511|32|79647ms|2489ms|Y|0107_70.2%,0202_29.8%
0089|  8.80%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|getTypeForFactoryBean|76|1464|7|77170ms|11024ms|R|0090_99.2%,2139_0.3%,0092_0.3%,2790_0.2%
0090|  8.73%|org.springframework.beans.factory.support.AbstractBeanFactory|getTypeForFactoryBean|76|1452|2|76537ms|38269ms|R|0092_93.7%,0014_6.3%
0091|  8.40%|com.taobao.hsf.app.api.util.HSFApiConsumerBean|init|69|1398|89|73691ms|828ms|B|0112_73.9%,0273_16.9%,0480_7.8%,0351_0.9%,2001_0.4%,4734_0.1%,4102_0.1%
0092|  8.39%|org.springframework.beans.factory.support.FactoryBeanRegistrySupport|getTypeForFactoryBean|73|1395|40|73533ms|1838ms|Y|0013_98.1%,0755_1.8%
0093|  8.37%|com.taobao.hsf.app.spring.util.HSFSpringConsumerBean|init|69|1392|87|73374ms|843ms|B|0091_100.0%
0094|  8.29%|org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory|autowireConstructor|78|1379|20|72689ms|3634ms|Y|0095_99.9%,0020_0.1%
0095|  8.28%|org.springframework.beans.factory.support.ConstructorResolver|autowireConstructor|78|1378|20|72636ms|3632ms|Y|0074_80.0%,0377_10.9%,0013_8.5%,0758_0.6%,0020_0.1%
0096|  8.21%|org.springframework.beans.factory.support.FactoryBeanRegistrySupport$1|run|71|1366|11|72004ms|6546ms|Y|0097_99.6%,2715_0.2%,5223_0.1%,4654_0.1%
0097|  8.18%|com.taobao.iopen.core.client.instance.factory.InstanceFactoryBean|getObjectType|71|1361|6|71740ms|11957ms|R|0098_100.0%
0098|  8.18%|com.taobao.iopen.core.client.instance.factory.InstanceFactoryBean|getObject|71|1361|6|71740ms|11957ms|R|0099_100.0%

5、核心问题

经过上面的分析,核心问题就指向了类加载。类加载耗时高是因为需要查找的jar包太多了。我检查了下自己的应用。alibee-shop依赖的二方包有1.5k,体积1.1G。一个优化思路是,精简依赖,去除无效jar包。总共依赖1.5k个jar,实际使用到478个,未加载的共1094个。所以核心问题是这个应用依赖的二方包,引入了大量无用依赖。在类加载阶段,类加载器会遍历Classpath下的所有jar来查找需要被加载的字节码,这就消耗了大量的时间。有的一个二方包引入了一白多个简洁依赖。使用maven的依赖分析可以看出,依赖层次非常的深。

5、解决方案

既然明确了问题所在。解决办法就比较简单了--去除依赖。去除依赖的根本办法是排查pom中的无用依赖。怎么指导哪些是无用依赖呢?因为是二方包引入的,作为使用方你不知道哪些是有效依赖,哪些是无用依赖。在这里可以使用arthas的类加载器分析功能。他可以列举出哪些jar包是被类加载器加载过的,哪些是尚未被加载的。未使用的jar被称为“尚未被加载的”,意思是可以线上的某些分支流量极小,大部分情况不会执行到。未被执行到是依赖的Class就不被加载,当被执行到时才会执行类加载动作。所以,尚未加载的jar不代表以后不会被使用到,不能简单的删除掉。排查掉依赖后,要进行全面的功能验证,保证自己的业务功能不受影响。但是为了在预发验证结论,我找到了未被加载的jar,在启动脚本中将这些jar进行删除。最终,应用包体积从1.1G降至300M。启动时间也缩短到了6分钟左右。

在启动脚本中删除未使用的jar仅是临时方案,最终方案是在pom中排除无用依赖。排除依赖是个非常繁琐的工作,因为二方包的依赖关系非常复杂,而且作为使用方你不清楚哪些应该被排除掉。所以在这里还是呼吁提供基础服务的团队,在提供二方包时,要保证依赖的简洁性,避免有的没的都依赖进来。这样对应使用方来说是巨大的负担。

6、总结

经历这么一个优化过程,我们发现,能定位到核心问题是非常关键的。而透过错综复杂的现象,能看到问题的本质是非常考验能力与经验的。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SpringBoot 是一个非常常用的 Java 应用框架,提供了许多便于开发的功能,但在启动时却常常存在较长的启动时间。在一些场景下要求应用启动速度较快,如在云原生应用中,快速启动可以提高资源的利用率和性能,因此需要对 SpringBoot 启动速度进行优化。 1. 确认依赖 在建立 SpringBoot 项目时,通常使用的是 Spring Initializr 工具,在选择依赖时,需要仔细考虑依赖的必要性,不必要的依赖会增加项目的启动时间。可以使用 mvn dependency:tree 命令来查看项目的依赖, 并考虑去掉不必要的依赖。 2. 开启 AOT 模式 AOT( Ahead of Time )模式将应用程序的字节码转换成机器码,在启动时直接执行机器码,减少了 JIT 编译的时间,从而加快了启动速度。可以使用 Spring Native 框架实现 AOT 编译,需注意的是,Spring Native 目前仅支持在 Linux 和 Mac OS 系统上构建应用,并要求应用需要使用 GraalVM 的版本。 3. 使用懒加载 在启动 SpringBoot 应用时,通常会创建许多 bean,这些 bean 可能与应用程序的当前请求无关。为了优化启动速度,可以将这些 bean 的创建延迟到第一次使用时再进行创建。这可以通过在 bean 上使用 @Lazy 注解或在配置文件中添加 spring.main.lazy-initialization=true 来实现。 4. 减少自动配置的数量 在 SpringBoot 项目中,通常会有许多自动配置类,在启动时 Spring 会扫描所有装配件以构建相应的 bean。通常我们只使用少量的自动配置类,因此可以通过在配置文件中添加 spring.autoconfigure.exclude 来排除一些不必要的自动配置类,减少启动时间。 5. 启用并发初始化 Spring 5.0 版本默认支持并行初始化的功能,可以同时初始化 bean,从而减少启动时间。如果使用的是 Spring 4.x 版本,可以使用 spring.parallel.init.enabled=true 属性来开启并行初始化。 在实际应用中,可能还有其他需要考虑的因素,如数据库连接池的配置、缓存的使用,通过对以上几个方面的优化,可以显著地降低 SpringBoot 应用启动时间,提高应用的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值