首頁>技術>

前言

在前面整理了一篇關於JVM故障診斷和處理工具,考慮到大部分的Java程式設計師都使用的時IntelliJ Idea,本篇就使用工具來實戰演練對IntelliJ Idea執行速度調優

調優前的執行狀態原始配置內容

要查詢idea原始配置檔案的路徑可以在VisualVM中的概述中檢視

原始配置內容:

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啟動外掛收集到的資訊:

IDEA啟動耗時 15s總共垃圾收集22次,耗時1.2s,其中新生代GC 17次,耗時324ms; 老年代GC 5次,耗時953ms載入類27526個,耗時 21s

按照這個資料來看也算是正常,15s 其實也在接受範圍內,由於本文主要演示效能調優,所以需要測試能否再快一些

開始嘗試最佳化調整記憶體來控制垃圾回收頻率

圖上我們可以看出,啟動引數指定的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->0K(181248K)表示 gc前已使用的記憶體大小 -> gc後已使用記憶體大小(該區域的總記憶體大小)

從日誌中我們看出每次Full GC都是因為Metadata GC Threshold,而Metaspace每次gc回收的記憶體幾乎沒有,僅僅是擴大了該區域的容量;找到了原因那就好辦了,新增如下的引數調整Metaspace的大小:

-XX:MetaspaceSize=256m

再次重啟Idea之後,發現Full GC沒有了,心情很爽

選擇垃圾收集器

從剛才的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以下的圖

19
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • GitHub+Docker Hub實現自動構建映象