本文基於《jvm性能權威指南》,總結了gc調優的相關知識點。然後希望能幫助大家快速上手jvm gc調優。
這裏不對垃圾收集的原理進行詳細介紹,只與性能調優相關。
一、垃圾收集器
1.各垃圾收集器主要特點
垃圾收集器 | 主要特點 | 啓用參數 |
---|---|---|
Serial垃圾收集器 | Client模式默認垃圾收集器; 單線程回收; MinorGC和FullGC都會暫停應用線程。 |
-XX:+UseSerialGC |
Parallel垃圾收集器 | Server模式默認垃圾收集器(jdk9前); 使用多線程回收; MinorGC和FullGC都會暫停應用線程,FullGC會對老年代空間進行壓縮整理。 |
-XX:+UseParallelGC, -XX:+UseParallelOldGC |
CMS垃圾收集器 | 使用多線程回收; MinorGC和老年代併發回收階段暫停應用線程,會有空間碎片化問題。 |
-XX:+UseParNewGC, -XX:+UseConcMarkSweepGC |
G1垃圾收集器 | jdk9後默認垃圾收集器; 使用非連續的區塊的方式管理堆內存; 多線程並行回收; MinorGC和老年代併發回收部分階段暫停應用線程。 |
-XX:+UseG1GC |
2.如何選擇垃圾收集器
- 小內存應用直接使用Serial,穩定高效
- 大部分應用在Parallel和CMS中選擇:
- Parallel+Parallel old組合主要關注吞吐量,適用批處理任務
- 在CPU資源充足的情況下使用CMS(CMS只使用老年代,需要配合serial/parallel new)
- CPU資源受限時CMS會發生併發模式失效的問題,退化到Serial Old
- 一般情況下,堆空間小於4G時,CMS比G1性能好,但超大堆下G1性能更好
二、GC調優基礎
1.分析工具
所有調優都是建立在對堆內存的使用情況有一定的瞭解的基礎上,所以我們首先需要使用一些工具對我們的java應用進行監控和分析,來瞭解到底出現了什麼問題:
- GC日誌
- -XX:+PrintGCDetails:打印GC日誌
- -XX:+PrintGCTimeStamps:GC日誌打印時間戳
- 堆實時監控
- 使用jstat -gcutil PID 1000 指令:每秒打印堆內存實時信息及GC情況
2.調整堆大小
- 考慮維度:
- GC頻率:過頻繁時需要增大堆大小
- 單次GC耗時:耗時過長需要考慮控制堆大小
- GC在整個時間中所佔百分比
- FullGC後剩餘可用空間:普遍經驗爲70%可用
- 調優參數:
- -Xms=N、-Xmx=N:堆初始值和最大值
- -Xmn=N:設置固定新生代大小
- -XX:NewRatio=N:設置新生代與老年代空間佔用比(此值爲分母)
- -XX:NewSize=N、-XX:MaxNewSize=N:設置新生代初始和最大值
3.永久代和元空間
- 元空間默認會使用儘可能多的空間,因此不用過於關注
- 永久代(jdk7及之前)
- -XX:PermSize=N、-XXMaxPermSize=N:設置固定永久代大小和最大值
- 元空間(jdk8以後)
- -XX:MetaspaceSize=N、-XX:MaxMetaspaceSize=N:設置固定元空間大小和最大值
4.控制併發
- 控制線程數:-XX:ParallelGCThreads=N,影響多線程操作:
- Parallel 收集新生代和老年代
- CMS:新生代收集(ParNewGC)、併發收集STW階段(非FullGC)
- G1 新生代收集、STW階段(非FullGC)
- 默認線程數計算(n爲cpu線程數):ParallelGCThreads = 8 + ( ( N-8 ) * 5 / 8 )
- 當機器CPU線程數比較多,此時默認線程數會比較大,而又同時運行多個jvm實例,這時需要手動控制線程數,避免過多垃圾回收線程併發運行。
5.自適應調整
JVM會自己根據以往的性能歷史進行性能參數調整
- -XX:-UseAdaptiveSizePolicy:關閉自適應調整功能(默認開啓)
- -XX:+PrintAdaptiveSizePolicy:打印自動調整信息
三、進階調優
1. Parallel GC
- Parallel收集器會根據指標自適應調整堆大小
- -XX:MaxGCPauseMillis=N:設置最大停頓時間
- 傾向於降低老年代大小,可能會觸發頻繁FullGC
- -XX:GCTimeRatio=N:設置應用線程運行時間與垃圾回收時間之比(默認99)
- N = 應用時間佔比 / (1 - 應用時間佔比 )
- N=99時,表示GC佔總時間1%
- 傾向於增大堆大小
- -XX:MaxGCPauseMillis=N:設置最大停頓時間
2. CMS
- 退化爲FullGC的情況:
- 併發模式失效:新生代發生垃圾回收,而老年代沒有足夠空間容納晉升對象
- 晉升失敗:老年代有足夠空間容納晉升對象,但是由於空間碎片化導致晉升失敗
- 永久代空間用盡
- 控制併發週期啓動時機
- -XX:+UseCMSInitiatingOccupancyOnly:不自動調整CMS垃圾收集週期(默認false)
- -XX:CMSInitiationOccupancyFraction=N:觸發併發收集週期的老年代空間佔用比閾值(默認70)
- 不要將閾值設置得過低,至少要比堆內活躍數據數多10%~20%,頻繁的併發週期中的STW會導致總體停頓過多
- 調整CMS後臺線程
- -XX:ConcGCThreads=N:後臺線程數目
- 默認根據ParallelGCThreads值計算得來:N = ( 3 + ParallelGCThreads ) / 4
- -XX:ConcGCThreads=N:後臺線程數目
3. G1
- 退化爲FullGC的情況:
- 併發模式失效:(參考cms)
- 晉升失敗:(參考cms)
- 疏散失敗:進行新生代垃圾收集時,Suvivor空間和老年代空間沒有足夠空間容納倖存對象
- 巨型對象分配失敗:對象所需內存過大,超過了單個區塊內存大小(或其他限制)
- 調整G1後臺線程數
- -XX:ConcGCThreads=N:後臺線程數目
- 默認根據ParallelGCThreads值計算得來:N = ( 2 + ParallelGCThreads ) / 4
- -XX:ConcGCThreads=N:後臺線程數目
- 調整垃圾收集頻率
- -XX:InitiationHeapOccupancyPercent=N:觸發併發收集週期的堆內存佔用比閾值(默認45)
- 調整混合式垃圾收集週期
- -XX:G1MixedGCLiveThresholdPercent=N:觸發分區回收標記的分區垃圾佔比閾值
- -XX:G1MixedGCCountTarget=N:最大混合式GC週期數(默認8)
- 減少此值可以幫助解決晉升失敗問題,代價是混合式GC週期停頓時間更長
- -XX:MaxGCPauseMillis=N:設置最大停頓時間(默認200ms)
4. Survivor空間及晉升:
- 設置Survivor空間大小
- -XX:InitialSurvivorRatio=N:初始Survivor空間大小佔比(N爲分母)
- survivor空間大小 = new_size / ( InitialSurvivorRatio + 2 )
- -XX:MinSurvivorRatio=N:Survivor最大大小(注意由於N是分母,雖然參數是min,但是卻是設置的最大值)
- -XX:InitialSurvivorRatio=N:初始Survivor空間大小佔比(N爲分母)
- 自動調節
- -XX:TargetSurvivorRatio=N:垃圾回收後空閒空間佔比
- 晉升閾值:對象在Survivor空間之間來回移動多少個GC週期後晉升到老年代
- -XX:InitialTenuringThreshold=N:初始晉升閾值
- -XX:MaxTenuringThreshold=N:最大晉升閾值
- JVM會持續的計算,在1和最大晉升閾值中選擇合適的值