JVM性能調(diào)優(yōu)實戰(zhàn):讓你的IntelliJ Idea縱享絲滑
本文已被Github倉庫收錄 https://github.com/silently9527/JavaCore
前言
在前面整理了一篇關(guān)于JVM故障診斷和處理工具,考慮到大部分的Java程序員都使用的是IntelliJ Idea,本篇就使用工具來實戰(zhàn)演練對IntelliJ Idea運行速度調(diào)優(yōu)
調(diào)優(yōu)前的運行狀態(tài)
原始配置內(nèi)容
要查詢idea原始配置文件的路徑可以在VisualVM中的概述中查看
原始配置內(nèi)容:
-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
打印啟動時間插件開發(fā)
需要直觀的看到優(yōu)化前和優(yōu)化后啟動時間的變化,所以需要簡單做一個Idea的插件開發(fā),關(guān)于Idea插件開發(fā)的流程建議參考我以前的文章《IDEA插件:多線程文件下載插件開發(fā)》
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>
優(yōu)化前的啟動信息與時間消耗
根據(jù)VisualGC和IDEA啟動插件收集到的信息:
- IDEA啟動耗時 15s 總共垃圾收集22次,耗時1.2s,其中新生代GC 17次,耗時324ms;
- 老年代GC 5次,耗時953ms 加載類27526個,耗時 21s
按照這個數(shù)據(jù)來看也算是正常,15s 其實也在接受范圍內(nèi),由于本文主要演示性能調(diào)優(yōu),所以需要測試能否在快一些
開始嘗試優(yōu)化
調(diào)整內(nèi)存來控制垃圾回收頻率
圖上我們可以看出,啟動參數(shù)指定的512m的內(nèi)存被分配到新生代的只有169m,由于IDEA是我們開發(fā)常用的工具,平時的編譯過程也需要足夠的內(nèi)存,所以我們需要先把總的內(nèi)存擴大,這里我設(shè)置最大的內(nèi)存 -Xmx1024m
,為了讓JVM在GC期間不需要再浪費時間再動態(tài)計算擴容大小,同時也設(shè)置了 -Xms1024m
;
在啟動的過程中Eden共發(fā)生了17次GC,為了減少新生代gc次數(shù),我把新生代的內(nèi)存大小設(shè)置成 -Xmn256m
;
重新啟動之后查看VisualGC,新生代gc次數(shù)從 17次 降低到了 7次,耗時從 324ms 降低到了 152ms。
在調(diào)整內(nèi)存前發(fā)生了5次Full GC,調(diào)整內(nèi)存后的依然還是有4次Full GC,但是從兩張圖我們可以看出,老年代的空間還有很多剩余,是不應(yīng)該發(fā)生Full GC的;考慮是否是代碼中有地方手動調(diào)用 System.gc()
出發(fā)了Full GC,所以添加了參數(shù) -XX:+DisableExplicitGC
,再次重新啟動IDEA,結(jié)果很失望,依然還有4次Full GC;
再次仔細(xì)觀察優(yōu)化前的圖,注意看 Last Cause: Metadata GC Threshold , 最后一次gc是應(yīng)該Metaspace區(qū)域內(nèi)存不夠發(fā)生的GC,為了驗證我們的猜想,打印出GC日志來看看。在 idea.vmoptions
中添加打印日志相關(guān)的參數(shù):
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:../gc.log
JVM的GC日志的主要參數(shù)包括如下幾個:
- -XX:+PrintGC 輸出GC日志
- -XX:+PrintGCDetails 輸出GC的詳細(xì)日志
- -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準(zhǔn)時間的形式)
- -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
- -XX:+PrintHeapAtGC 在進行GC的前后打印出堆的信息
- -Xloggc:../logs/gc.log 日志文件的輸出路徑
重新啟動idea,查看gc.log
其中 PSYoungGen:
表示新生代使用的ParallelScavenge垃圾收集器, 31416K->0K(181248K)
表示 gc前已使用的內(nèi)存大小 -> gc后已使用內(nèi)存大?。ㄔ搮^(qū)域的總內(nèi)存大?。?/p>
從日志中我們看出每次Full GC都是因為 Metadata GC Threshold
,而Metaspace每次gc回收的內(nèi)存幾乎沒有,僅僅是擴大了該區(qū)域的容量;找到了原因那就好辦了,添加如下的參數(shù)調(diào)整Metaspace的大小:
-XX:MetaspaceSize=256m
再次重啟Idea之后,發(fā)現(xiàn)Full GC沒有了,心情很爽
測試打開大項目點擊編譯代碼,發(fā)現(xiàn)自己的idea卡死了,查看VisualGC之后發(fā)現(xiàn)堆內(nèi)存都還有空閑,只有Metaspace被全部占滿了,所以是自己給的最大空間設(shè)置太小,所以直接去掉了 -XX:MaxMetaspaceSize=256m
選擇垃圾收集器
從剛才的gc日志中,我們可以發(fā)現(xiàn)默認(rèn)使用的是ParallelScavenge + Parallel Old垃圾收集器,這個組合注重的是吞吐量,這里我們嘗試換一個注重低延時的垃圾收集器試一試
ParNew + CMS
在 idea.vmoptions
中添加如下配置:
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
重啟IDEA之后查看VisualGC
很尷尬,同樣發(fā)生了6次gc, ParallelScavenge + Parallel Old
的組合耗時197ms,而 ParNew + CMS
的組合耗時379ms;雖然是這個結(jié)果,但是我們需要考慮當(dāng)前只發(fā)生了MinorGC,如果發(fā)生FullGC了結(jié)果又會如何了,大家可以自己測試一下
G1
我們在換一個最新的G1垃圾回收器試試,在 idea.vmoptions
中添加如下配置:
-XX:+UseG1GC
這個結(jié)果好像也還是要慢一點點,自己多次測試過這兩個垃圾回收器,雖然每次結(jié)果都不一樣,相差不遠(yuǎn),所以垃圾回收器可以自己選擇,這里我們選擇的是G1
類加載時間優(yōu)化
根據(jù)之前的分析,idea啟動加載類27526個,耗時 21s,這個我們有辦法能優(yōu)化一下嗎?因為idea是常用的開發(fā)工具,經(jīng)常很多人的使用,我們可以認(rèn)為它的代碼是安全的,是否符合當(dāng)前虛擬機的要求,不會危害虛擬機的安全,所以我們使用參數(shù) -Xverify:none
來禁用字節(jié)碼的驗證過程
重啟IDEA
耗時下降到了11s,效果還是比較明顯的
總結(jié)
做完了所有優(yōu)化之后,經(jīng)過多次重啟測試,平均的啟動時間下降到了11s,為了安慰我本次操作沒有白辛苦,搞一張11s以下的圖
我已經(jīng)從零開始手寫了簡易版springmvc,以及編寫了詳細(xì)的說明文檔,希望能夠幫助伙伴們深入理解springmvc核心原理.
源碼獲取地址:我把開源的項目代碼都已經(jīng)放到了Git倉庫,Github倉庫地址:https://github.com/silently9527 ,碼云倉庫地址:https://gitee.com/silently9527 ,
到此這篇關(guān)于JVM性能調(diào)優(yōu)實戰(zhàn):讓你的IntelliJ Idea縱享絲滑的文章就介紹到這了,更多相關(guān)JVM性能調(diào)優(yōu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?Cloud實現(xiàn)灰度發(fā)布的示例代碼
這篇文章主要為大家詳細(xì)介紹了Spring?Cloud實現(xiàn)灰度發(fā)布的相關(guān)知識,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以了解一下2023-09-09springboot項目中實現(xiàn)訪問druid內(nèi)置監(jiān)控頁面
這篇文章主要介紹了springboot項目中實現(xiàn)訪問druid內(nèi)置監(jiān)控頁面的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06java中的instanceof關(guān)鍵字詳細(xì)解讀
這篇文章主要介紹了java中的instanceof關(guān)鍵字詳細(xì)解讀,instanceof 是 Java 的保留關(guān)鍵字,它的作用是測試它左邊的對象是否是它右邊的類的實例,返回 boolean 的數(shù)據(jù)類型,需要的朋友可以參考下2024-01-01