摘要
本文記錄GC調試的一次實驗過程和結果。
GC知識要點回顧
問題1:爲什麼要調試GC參數?
在32核處理器的系統上,10%的GC時間導致75%的吞吐量損失。所以在大型系統上,調試GC是以小博大的不錯選擇。'small improvements in reducing
such a bottleneck can produce large gains in performance.'
問題2:怎麼樣調試GC?
調試GC,有三個主要的參數:
選擇合適的GC Collector
整個JVM Heap堆的大小
Young Generation的大小(-Xmn?m or -XX:NewRatio=?)
問題3:有哪些不同的GC Collector?
Tony Printezis (JVM大牛)在Garbage Collection in the Java HotSpot Virtual Machine有圖爲證,還有一篇更早的sun開發人員介紹GC調試也是有圖爲證
neo4j總結如下
GC shortname | Generation | Command line parameter | Comment |
---|---|---|---|
Copy | Young |
| The Copying collector |
MarkSweepCompact | Tenured |
| The Mark and Sweep Compactor |
ConcurrentMarkSweep | Tenured |
| The Concurrent Mark and Sweep Compactor |
ParNew | Young |
| The parallel Young Generation Collector—can only be used with the Concurrent mark and sweep compactor. |
PS Scavenge | Young |
| The parallel object scavenger |
PS MarkSweep | Tenured |
| The parallel mark and sweep collector |
簡而言之,Young和Tenured各種三種Collector,分別是
Serial 單線程
Parallel 多線程並行, GC線程和App線程取一運行,即GC要Stop the (app) world。
Concurrent 多線程併發,GC線程和App線程可同時運行。(注: Young generation 沒有CMS,取而代之的是可和CMS(Old)一起運行的ParNew)
問題4:如何選擇Collector?
Serial可以直接排除掉,現在最普通的服務器也有雙核64位\8G內存,默認的Collector是PS Scavenge和PS MarkSweep。所以Collector在並行(Parallel)和併發(Concurrent)兩者之間選擇。
問題5:選擇的標準(參數指標)是什麼?如何得到這些參數值(How to measure it)?
throughput和latency。garbage-collection-in-java-part-3從GC的耗時給出了吞吐量和響應速度的公式
Total Execution Time = Useful Time + Paused Time
throughput = Useful Time / Total Execution Time
latency = average paused time
如何得到Useful time 和 Paused Time?即如何得到JVM的GC時間,有以下幾種方式
GC Log
打印GC log,java 啓動參數中加入下面的語句(本文爲tomcat應用)。GC Log 記錄每次GC時間,可根據GC Log計算平均GC時間和累積GC時間。
[plain] view plain copy
CATALINA_OPTS="$CATALINA_OPTS -verbose:gc -Xloggc:/usr/local/tomcat/gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps"
JDK自帶工具,java 啓動參數中加入下面的語句(本文爲tomcat應用),然後在監控端可以遠程連接1090端口。在內存一項,有累積GC時間和次數。注意在以min爲單位顯示時,只顯示整數部分,如1min20s顯示爲1min。
[plain] view plain copy
CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
JVM監控工具,未同JDK一起發佈,可在JVisualvm(JDK自帶)中以插件的方式使用,本文爲獨立使用。有累積GC時間和次數,並有曲線圖直觀顯示。
首先在Server端啓動jstatd
[plain] view plain copy
vi jstatd.all.policy
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
jstatd -J-Djava.security.policy=jstatd.all.policy
然後在監控啓動VisualGC,遠程連接服務端進程id
visualgc [email protected]
問題5:應用請求的吞吐量和響應是否可以反映JVM的性能?
正是我們調優的目標。本文使用Jmeter來做壓力測試,並給出吞吐量和響應 report。
測試
硬件環境
|
|
Test case
使用用Jmeter壓力測試
共6個client,每個client啓動30個線程發送請求
每個請求從16種測試樣例中隨機挑選一個,發送到server端
測試持續10min
參數值
server使用默認GC(PS Scavenge和PS MarkSweep)
server使用CMS(-XX:+UseConcMarkSweepGC-XX:+UseParNewGC)
server使用CMS(-XX:+UseConcMarkSweepGC -XX:+UseParNewGC),設置Young generation的大小爲200m(-Xmn200m)
server使用CMS(-XX:+UseConcMarkSweepGC -XX:+UseParNewGC),設置Young generation的大小爲600m(-Xmn600m)
觀察值
Jmeter請求的summary report
server端累積GC時間和次數
測試結果
1) CMS和Parallel比較
1.1) 吞吐量和響應
(PS
Scavenge和PS MarkSweep)
(ParNew和CMS)
從 Jmeter的report中可以看出, 使用CMS後吞吐量(對應總的請求數)下降18%,而最大響應時間(包括最小響應時間)有近30%的提升(變小)。這驗證了Tony Printezis在Step-by-Step:Garbage Collection Tuning in the Java HotSpot Virtual Machine中說使用CMS應用的吞吐量會相對下降,但有更好的最差響應時間。
Expect longer young GC times
Due to slower allocations into the old gen
Expect better worst-case latencies
CMS does its work mostly-concurrently
Shorter worst-case pauses
Expect lower throughput
CMS does more work
在官方的JVM性能調優中給出的建議也是,如果你的應用對峯值處理有要求,而對一兩秒的停頓可以接受,則使用(-XX:+UseParallelGC);如果應用對響應有更高的要求,停頓最好小於一秒,則使用(-XX:+UseConcMarkSweepGC)。
1.2) GC 累積時間和次數
(PS
Scavenge和PS MarkSweep)
(ParNew和CMS)
PS累積GC時間(visualgc)爲1min25s,其中Eden 189次,共52s;old 13次,共33s。
CMS
累積GC(visualgc)爲2min2s,其中Eden 2333次,共1min46s;old 55次,共16s。(Jconsole和GC log卻顯示沒有Full GC,從understanding cms gc logs和jstat顯示的full GC次數與CMS週期的關係中我推測visualgc與jstat顯示一致,都是統計old的回收次數;而Full
GC則是Young和Old一起回收,在其他類型的GC裏,Old只有Full GC時才觸發)。
可以看到PS的GC頻率相對低,但每次GC時間長,每次Full在3s左右徘徊,Yong在0.3s左右;CMS則是短頻快,頻繁快速回收,yong在0.03s(<0.1s)左右,old<0.5s。從JMeter上,使用PS GC,Request Report會有間歇性的停頓,即server沒有任何響應;CMS則相對較少,停頓不那麼明顯。
2) CMS下不同Xmn的比較
由於CMS Young太多頻繁,又測試了分別調整Xmn爲200m和600m之後的結果。200m是仿照cassandra中100m * cpu #來設置Young gen的大小;600m則是與PS下的Young gen一致。
200m
600m
隨着Young gen的增大(40m -> 200m -> 600m),Young 的回收次數減少,Old的回收次數增加,總體GC累積時間下降,應用吞吐量上升,最差響應時間變慢(即便和PS比較也更差,是我的測試有問題?)。
結論
app停頓3s是不可接受的,因此傾向於使用CMS;CMS的default young gen相當小,於是設置Xmn。對於更加Prefer響應的應用,下面配置是否是黃金標配:
JVM_OPTS="$JVM_OPTS -XX:+UseParNewGC" JVM_OPTS="$JVM_OPTS -XX:+UseConcMarkSweepGC" JVM_OPTS="$JVM_OPTS -XX:+CMSParallelRemarkEnabled" JVM_OPTS="$JVM_OPTS -XX:SurvivorRatio=8" JVM_OPTS="$JVM_OPTS -XX:MaxTenuringThreshold=1"JVM_OPTS="$JVM_OPTS -XX:CMSInitiatingOccupancyFraction=75"JVM_OPTS="$JVM_OPTS -XX:+UseCMSInitiatingOccupancyOnly"