延遲指服務器處理一個請求所花費的時間,單位一般是ms、s。
本文主要講降低延遲可以做的服務器端JVM優化。
JVM延遲優化
新生代
新生代大小決定了應用平均延遲
如果平均Minor GC持續時間大於應用程序平均延遲性要求,可以適當減小新生代空間大小;
如果Minor GC頻率大於應用程序平均延遲性要求,可以適當增大新生代空間;
老年代
老年代大小決定了應用最差延遲
FullGC頻率大於應用程序最大FullGC頻率要求,可以適當增大老年代空間大小;
FullGC持續時間大於應用程序最差延遲性要求,可以使用CMS垃圾收集器;
CMS收集器調優
CMS主要特性
CMS為老年代收集器,收集線程能與應用程序線程實現最大的並行度,降低了延遲。
CMS並不進行壓縮,所以老年代使用空閑列表分配內存,在一定程度上增加了新生代晉升老年代時,耗費的時間。
但一旦老年代溢出就會觸發Stop-The-World壓縮式垃圾收集。
所以,CMS的優化大概有下面幾點:
1. 避免用盡老年代空間;
2. 解決老年代碎片化問題,解決方法包括壓縮、MinorGC回收原則;
3. 降低初始標記階段和重新標記階段占用的時間,因為這兩個階段應用程序線程會被阻塞;
Survivor空間調優
調整Survivor空間,是為了讓其有足夠空間容納存活對象足夠長的時間,直到幾個周期后對象老化,解決上面的第一、二條問題。
Survivor空間的大小可以通過下面這個參數調整:
-XX:SurvivorRatio=<ratio>
<ratio>必須大於0,-XX:SurvivorRatio=<ratio>表示單個Survivor和Eden的比率。
survivor空間大小 = –Xmn/(-XX:SurvivorRatio=<ratio> + 2)
監控晉升閾值
-XX:MaxTenuringThreshold=n //設置最大晉升閾值
-XX:+PrintTenuringDistribution //打印晉升的分布或對象年齡分布到GC日志
如下面這個晉升日志:
Desired survivor size 1114112 bytes, new threshold 1 (max 6) - age 1: 2213864 bytes, 2213864 total
Desired survivor size 1114112 bytes是Survivor空間大小 * 目標存活率(可以設定,默認50%)
new threshold 1 JVM內部計算出的晉升閾值 (max 6)設置的晉升閾值
age 1(對象年齡代) 2213864 bytes(這個年齡對象總大小), 2213864 total(所有年齡對象總大小)
Survivor目標存活率:指Minor GC后Survivor空間占用比率,如果太大,下次Minor GC后存活的對象就有可能放不下
從這個日志可以看出,存活對象2213864bytes大於期望Survivor大小為1124112bytes,所以對象年齡為1時,就提升到了老年代。
解決這個問題的方法就是增大Survivor空間,存活對象大小 / 目標存活率 = Survivor空間大小,本例中大概4.5M,所以修改JVM參數為:
-Xmx512m -Xms512m -Xmn50m -XX:SurvivorRatio=5
調整后的晉升日志大概如下,這個已不是上面那個應用了,所以Survivor空間不一樣,能說明問題就行:
Desired survivor size 87359488 bytes, new threshold 15 (max 15) - age 1: 20701528 bytes, 20701528 total - age 2: 4924656 bytes, 25626184 total - age 3: 3261000 bytes, 28887184 total - age 4: 7941120 bytes, 36828304 total
調整Survivor空間的一個重要原則:
調整Survivor空間時,應保持Eden空間不變,否則會導致Minor GC頻率變小。
初始化CMS收集周期
CMS中發生的Stop-The-World壓縮式垃圾收集可以在GC日志中查找並發模式失效(concurrent mode failure)定位。
如果有這個問題,就需要調整CMS收集周期。
-XX:CMSInitiatingOccupancyFraction=<percent> //設置CMS垃圾收集周期在老年代空間占用達到多少是啟動
-XX:+UseCMSInitiatingOccupancyOnly //告知JVM總是使用 CMSInitiatingOccupancyFraction比率啟動CMS,否則只是在CMS第一次啟動時
原則:CMSInitiatingOccupancyFraction設置的比率至少是老年代活躍數據的1.5倍
如果老年代空間消耗的比較慢,可以稍晚啟動CMS,即將CMSInitiatingOccupancyFraction設定的更大;
如果老年代空間消耗的很快,可以將CMSInitiatingOccupancyFraction設定的更小,但不能低於活躍數據占用的比率;
降低CMS重新標記階段占用時間
-XX:ParallelGCThreads //控制重新標記的線程數,建議將CMS的收集線程數設置的小於默認值,否則大量GC線程會影響應用性能
-XX:+CMSScavengeBeforeRemark //在CMS進入重新標記階段先進行一次Minor GC,可以減少引用老年代的新生代對象數量,降低重新標記階段的工作量
-XX:+ParallelRefProcEnabled //使用多線程處理引用,不過在JDK6u25和6u26上有BUG