最近在阅读《深入理解Java虚拟机》这本书,看到了其中的Eclipse运行速度调优,属实手痒难耐,决定对IDEA操作一番。一方面作为练习,一方面巩固知识。
本文章仅作记录jvm的练习和idea的使用技巧。欢迎各位指教和交流。
JVM调优
准备工作
运行环境
- 64位Windows 10系统、16G内存。
- IDEA 2022.3.2,不同版本的IDEA使用的内置JVM不同。
- Java visualVM 2.1.5. + visual gc插件
- notepad++,用来打开idea.vmoptions
初始配置:idea.vmoptions,上图中可以看到路径。接下来的操作需要修改这个文件,记得备份。
-Xms128m
-Xmx1024m
-XX:ReservedCodeCacheSize=512m
-XX:+IgnoreUnrecognizedVMOptions
-XX:+UseG1GC
-XX:SoftRefLRUPolicyMSPerMB=50
-XX:CICompilerCount=2
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-ea
-Dsun.io.useCanonCaches=false
-Djdk.http.auth.tunneling.disabledSchemes=""
-Djdk.attach.allowAttachSelf=true
-Djdk.module.illegalAccess.silent=true
-Dkotlinx.coroutines.debug=off
-XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
-XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED
--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED
启动时间显示插件。
分析
开启GC日志
在idea.vmoptions
中添加
-verbose:gc
-XX: +PrintCommandLineFlags
-Xloggc:C:\Users\xxx\Desktop\idea_gc.log
- -verbose:gc:在控制台输出GC情况
- -XX: +PrintCommandLineFlags:程序运行时,打印虚拟机接受到的命令行显式参数
- -Xloggc:[指定文件位置]:打印文件到指定路径
打开idea
此时我们可以获得以下信息。
- 没有进行堆的扩容。
- 进行了52次GC。52次都是新生代的GC,没有比较耗时的老年代GC。
- 开启时长3s左右
- 使用了GC收集器
- IDEA2022.3.2确实强大
实战
我们的调优思路可以从几个方面进行
- 更换Java版本,选择合适的垃圾收集器
- 避免堆的频繁扩容,调整堆的大小,减少GC的次数
- 调整元空间的大小
- 类加载时间优化
- 视情况调整老年代的占比
1、更换Java版本,选择合适的垃圾收集器
在 JDK 8 中,Parallel是默认设置,但在 JDK 9 中改为了 G1。从那以后,G1 的改进速度就超过了 Parallel,但在有些情况下可能 Parallel 仍然是最佳选择。ZGC(从 JDK 15 开始正式使用)的加入,成为了第三种高性能替代方案。
IDEA2022.3.2版本内置的JDK17默认使用GC垃圾收集器,这里我们可以测试ZGC垃圾收集器。
将配置文件中的-XX:+UseG1GC
换成-XX:+UseZGC
。笔者的分析工具从1.8升级到2.1.5(目前官网最新版),都无法查看ZGC的Visual GC。所以我们分析以下日志。
- gc次数明显减少。但并不知道耗时是否低于G1。后续的使用中发现有卡顿的现象,慎用。
[0.024s][info][gc] Using The Z Garbage Collector
[1.202s][info][gc] GC(0) Garbage Collection (Metadata GC Threshold) 88M(9%)->106M(10%)
[1.562s][info][gc] GC(1) Garbage Collection (Warmup) 112M(11%)->86M(8%)
[2.712s][info][gc] GC(2) Garbage Collection (Warmup) 216M(21%)->226M(22%)
[3.799s][info][gc] GC(3) Garbage Collection (Warmup) 332M(32%)->194M(19%)
[9.127s][info][gc] Allocation Stall (AWT-EventQueue-0) 137.776ms
[9.127s][info][gc] Allocation Stall (DefaultDispatcher-worker-7) 69.313ms
[9.127s][info][gc] Allocation Stall (DefaultDispatcher-worker-59) 19.475ms
[9.127s][info][gc] Allocation Stall (DefaultDispatcher-worker-29) 222.981ms
[9.127s][info][gc] Allocation Stall (DefaultDispatcher-worker-9) 8.958ms
[9.173s][info][gc] Allocation Stall (DefaultDispatcher-worker-29) 6.145ms
[9.173s][info][gc] Allocation Stall (AWT-EventQueue-0) 32.246ms
[9.173s][info][gc] Allocation Stall (DefaultDispatcher-worker-59) 2.432ms
[9.224s][info][gc] Relocation Stall (DefaultDispatcher-worker-7) 58.346ms
[9.279s][info][gc] GC(4) Garbage Collection (Metadata GC Threshold) 870M(85%)->454M(44%)
[9.936s][info][gc] GC(5) Garbage Collection (Allocation Stall) 460M(45%)->316M(31%)
[10.862s][info][gc] GC(6) Garbage Collection (Allocation Rate) 366M(36%)->240M(23%)
[16.528s][info][gc] GC(7) Garbage Collection (Allocation Rate) 746M(73%)->384M(38%)
[21.600s][info][gc] GC(8) Garbage Collection (Allocation Rate) 822M(80%)->428M(42%)
[22.702s][info][gc] GC(9) Garbage Collection (Allocation Rate) 518M(51%)->354M(35%)
[24.586s][info][gc] GC(10) Garbage Collection (Allocation Rate) 410M(40%)->420M(41%)
[25.551s][info][gc] GC(11) Garbage Collection (Allocation Rate) 452M(44%)->344M(34%)
[27.288s][info][gc] GC(12) Garbage Collection (Allocation Rate) 530M(52%)->376M(37%)
[29.007s][info][gc] GC(13) Garbage Collection (Allocation Rate) 606M(59%)->448M(44%)
[29.607s][info][gc] GC(14) Garbage Collection (Allocation Rate) 470M(46%)->458M(45%)
[30.541s][info][gc] GC(15) Garbage Collection (Allocation Rate) 618M(60%)->422M(41%)
[31.800s][info][gc] GC(16) Garbage Collection (Allocation Rate) 668M(65%)->426M(42%)
[32.724s][info][gc] GC(17) Garbage Collection (Allocation Rate) 634M(62%)->482M(47%)
[33.906s][info][gc] GC(18) Garbage Collection (Allocation Rate) 670M(65%)->434M(42%)
[36.047s][info][gc] GC(19) Garbage Collection (Allocation Rate) 784M(77%)->386M(38%)
[39.570s][info][gc] GC(20) Garbage Collection (Allocation Rate) 838M(82%)->418M(41%)
[41.805s][info][gc] GC(21) Garbage Collection (Allocation Rate) 630M(62%)->356M(35%)
[76.509s][info][gc] GC(22) Garbage Collection (Proactive) 540M(53%)->358M(35%)
[110.519s][info][gc] GC(23) Garbage Collection (Proactive) 528M(52%)->334M(33%)
[143.496s][info][gc] GC(24) Garbage Collection (Proactive) 494M(48%)->334M(33%)
2、避免堆的频繁扩容,调整堆的大小,减少GC的次数
我们可以通过固定堆的大小,减少GC,避免频繁扩容。可以看到,比起最开始,Eden的GC次数下降了一半,时间减少了100ms。然而,我们发现,元空间几乎是满的,日志里也有几次元空间的GC。
-Xms2028m 初始堆内存
-Xmx2028m 最大堆内存
3. 调整元空间的大小
-XX:MetaspaceSize=512m
设置元空间初始大小。看得出,刚才元空间的四次GC消失了,分配大小没有变化,GC时间只减少一点点,也可能没有变化。所以这一步可以忽略。
4. 类加载时间优化
根据之前的日志,可以看出,CLass Loader的时间基本上在40~50s。这里我们默认Idea的代码安全。通过-Xverify:none
关闭验证字节码的验证。
经过测试,CLass Loader基本在40s左右了。
总结
一番调试,发现今天翻车了。上次的成功调试,是因为我当时用的是idea2021,它内置的java版本远不及现在的17。最新版本的idea,除了将堆调大点外,其他的优化已经不是初学者(我)能进行的。技术在迭代,性能也在不断的上升,可以说,互联网的领军者已经在不断突破硬件利用率的瓶颈,而紧跟其后的就是软件的更新。希望诸君通过自己的努力,能在诺大的互联网,拥有一席之地。
IDEA使用调整
热加载
在过去很长一段时间中,笔者陷入了只修改一处代码,却需要重启整个项目的困扰。偶然发现了IDEA的一个强大功能:修改一处,只编译一处。
注意:1、笔者之前开启热部署的时候,已经修改了一些配置,对接下的情况应该会有影响,但是在本次实验的项目并没有引入热部署依赖,所以读者可以自行查看一下热部署需要开启的配置,决定是否开启。2、需在Debug模式下,新增方法、变量,修改配置文件,模板引擎页面默认开启缓存的问题,这时候只能重启。
我们在Debug模式
下,在接口中加了一行代码。
三种编译方式:整个项目,单个模块,单个文件。
选择编译方式:单个文件。如果是灰色,可以随便修改点东西再撤销。
可以看到编译完成
测试
忽略不需要启动的项目
- 我们可以忽略不需要编译的项目,提高maven的编译速度。
实际开发中,并不是每一个项目都需要去使用,我们可以排除那些无需编译的项目
视情况移出,建议点否,移除会导致其他地方的引用需要重新Reload
当然,如果修改了被忽略的模块,需要手动编译一下。
减少索引时间
开启项目的时候需要加载文件索引,这是个相当耗时的操作。我们可以通过排除压根用不上的模块来提升体验。