JVM性能调优实战:让你的IntelliJ Idea纵享丝滑

本文已被Github仓库收录 https://github.com/silently9527/JavaCorejava

微信公众号:贝塔学Javagit

前言

在前面整理了一篇关于JVM故障诊断和处理工具,考虑到大部分的Java程序员都使用的时IntelliJ Idea,本篇就使用工具来实战演练对IntelliJ Idea运行速度调优程序员

调优前的运行状态

原始配置内容

要查询idea原始配置文件的路径能够在VisualVM中的概述中查看github

原始配置内容:spring

-XX:ReservedCodeCacheSize=240m
-XX:+UseCompressedOops
-Dfile.encoding=UTF-8
-XX:SoftRefLRUPolicyMSPerMB=50
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow

-XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
-XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof

-Xmx512m

打印启动时间插件开发

须要直观的看到优化前和优化后启动时间的变化,因此须要简单作一个Idea的插件开发,关于Idea插件开发的流程建议参考我之前的文章《IDEA插件:多线程文件下载插件开发 》安全

JVM的启动时间到全部组件初始化完成后的时间就看作是IDEA的启动时间,代码以下微信

public class MyApplicationInitializedListener implements ApplicationInitializedListener {
    @Override
    public void componentsInitialized() {
        RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
        long startTime = bean.getStartTime();
        long costTime = System.currentTimeMillis() - startTime;

        Messages.showMessageDialog("毫秒:" + costTime, "启动耗时", Messages.getInformationIcon());
    }
}

plugin.xml中添加以下代码:多线程

<extensions defaultextensionns="com.intellij">
    <applicationinitializedlistener id="MyApplicationInitializedListener" implementation="cn.silently9527.MyApplicationInitializedListener" />
</extensions>

优化前的启动信息与时间消耗

根据VisualGC和IDEA启动插件收集到的信息:mvc

  • IDEA启动耗时 15s
  • 总共垃圾收集22次,耗时1.2s,其中新生代GC 17次,耗时324ms; 老年代GC 5次,耗时953ms
  • 加载类27526个,耗时 21s

> 按照这个数据来看也算是正常,15s 其实也在接受范围内,因为本文主要演示性能调优,因此须要测试可否在快一些app

开始尝试优化

调整内存来控制垃圾回收频率

图上咱们能够看出,启动参数指定的512m的内存被分配到新生代的只有169m,因为IDEA是咱们开发经常使用的工具,平时的编译过程也须要足够的内存,因此咱们须要先把总的内存扩大,这里我设置最大的内存-Xmx1024m,为了让JVM在GC期间不须要再浪费时间再动态计算扩容大小,同时也设置了-Xms1024m

在启动的过程当中Eden共发生了17次GC,为了减小新生代gc次数,我把新生代的内存大小设置成-Xmn256m;

从新启动以后查看VisualGC,新生代gc次数从 17次 下降到了 7次,耗时从 324ms 下降到了 152ms。

在调整内存前发生了5次Full GC,调整内存后的依然仍是有4次Full GC,可是从两张图咱们能够看出,老年代的空间还有不少剩余,是不该该发生Full GC的;考虑是不是代码中有地方手动调用System.gc()出发了Full GC,因此添加了参数-XX:+DisableExplicitGC,再次从新启动IDEA,结果很失望,依然还有4次Full GC;

再次仔细观察优化前的图,注意看 Last Cause: Metadata GC Threshold , 最后一次gc是应该Metaspace区域内存不够发生的GC,为了验证咱们的猜测,打印出GC日志来看看。在idea.vmoptions中添加打印日志相关的参数:

-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:../gc.log

JVM的GC日志的主要参数包括以下几个:

  • -XX:+PrintGC 输出GC日志
  • -XX:+PrintGCDetails 输出GC的详细日志
  • -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
  • -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
  • -XX:+PrintHeapAtGC 在进行GC的先后打印出堆的信息
  • -Xloggc:../logs/gc.log 日志文件的输出路径

从新启动idea,查看gc.log

> 其中PSYoungGen:表示新生代使用的ParallelScavenge垃圾收集器,31416K-&gt;0K(181248K)表示 gc前已使用的内存大小 -> gc后已使用内存大小(该区域的总内存大小)

从日志中咱们看出每次Full GC都是由于Metadata GC Threshold,而Metaspace每次gc回收的内存几乎没有,仅仅是扩大了该区域的容量;找到了缘由那就好办了,添加以下的参数调整Metaspace的大小:

-XX:MetaspaceSize=256m

再次重启Idea以后,发现Full GC没有了,心情很爽

测试打开大项目点击编译代码,发现本身的idea卡死了,查看VisualGC以后发现堆内存都还有空闲,只有Metaspace被所有占满了,因此是本身给的最大空间设置过小,因此直接去掉了-XX:MaxMetaspaceSize=256m

选择垃圾收集器

从刚才的gc日志中,咱们能够发现默认使用的是ParallelScavenge + Parallel Old垃圾收集器,这个组合注重的是吞吐量,这里咱们尝试换一个注重低延时的垃圾收集器试一试

  • ParNew + CMS 在idea.vmoptions中添加以下配置:
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC

重启IDEA以后查看VisualGC

很尴尬,一样发生了6次gc,ParallelScavenge + Parallel Old的组合耗时197ms,而ParNew + CMS的组合耗时379ms;虽然是这个结果,可是咱们须要考虑当前只发生了MinorGC,若是发生FullGC告终果又会如何了,你们能够本身测试一下

  • G1 咱们在换一个最新的G1垃圾回收器试试,在idea.vmoptions中添加以下配置:
-XX:+UseG1GC

这个结果好像也仍是要慢一点点,本身屡次测试过这两个垃圾回收器,虽然每次结果都不同,相差不远,因此垃圾回收器能够本身选择,这里咱们选择的是G1

类加载时间优化

根据以前的分析,idea启动加载类27526个,耗时 21s,这个咱们有办法能优化一下吗?由于idea是经常使用的开发工具,常常不少人的使用,咱们能够认为它的代码是安全的,是否符合当前虚拟机的要求,不会危害虚拟机的安全,因此咱们使用参数-Xverify:none来禁用字节码的验证过程

重启IDEA

耗时降低到了11s,效果仍是比较明显的

总结

作完了全部优化以后,通过屡次重启测试,平均的启动时间降低到了11s,为了安慰我本次操做没有白辛苦,搞一张11s如下的图


写到最后(点关注,不迷路)

文中或许会存在或多或少的不足、错误之处,有建议或者意见也很是欢迎你们在评论交流。

最后,白嫖很差,创做不易,但愿朋友们能够点赞评论关注三连,由于这些就是我分享的所有动力来源🙏

原创不易 转载请注明出处:https://silently9527.cn/archives/102


我已经从零开始手写了简易版springmvc,以及编写了详细的说明文档,但愿可以帮助伙伴们深刻理解springmvc核心原理,有须要的朋友欢迎关注公众号:贝塔学JAVA ,回复源码便可

相关文章
相关标签/搜索