記一次GC優化

現象

young gc時間達到了150-500ms之間,每個服務器的時間不一樣,都在這個區間,監控的tp99和tp999有明顯的毛刺

環境

docker環境,服務器規格:2c4g,容器:tomcat8,jdk:8,回收器:cms

排查過程

1、首先想到查看gc日誌,發現有如下日誌:

[GC (Allocation Failure) [ParNew: 561827K->2873K(629120K), 0.4622482 secs] 1149586K->590676K(2027264K), 0.4626134 secs] [Times: user=0.92 sys=0.00, real=0.46 secs]

可以看到長達400多ms

2、然後想,難道是新生代內存太多了,通過jmap head命令把當前堆的信息輸出到文件,查看:

可以看到新生代並不大,根據前面的GC日誌,也就回收了500多M的對象,不可能這麼慢

3、然後想,難道docker負載比較大,查看docker服務器信息,cpu、內存、網絡、磁盤都正常

4、既然docker本身很正常,哪就奇怪了,是什麼導致young gc比較慢呢?做什麼事,耗費了時間呢?但又感覺無從下手

5、後與架構師聊了下這個情況,提到gc的線程數,我說是默認的,默認的情況下,gc會開啓服務器的cpu的核數的個數的線程數,這裏要最關鍵的是取的是物理機的核數,而不是docker的核數,我查了下這個docker宿主機的核數是60,意味着什麼,我想大家都知道了,開啓了太多的線程,開啓線程也需要時間,線程之間的競爭也多起來,時間自然消耗就大了

解決

指定gc線程數和編譯線程數

-XX:ParallelGCThreads=2 -XX:CICompilerCount=2

觀察一段時間,發現young gc時間下降到了20-60ms之間的水平,提高了6,7倍吧,再看監控的tp99和tp999瞬間絲滑了很多

額外收穫

發現之前,容器重啓cpu彪高的問題也沒消失了,原來是它的鍋

總結

容器最佳配置

1、堆內存配置

1C2G容器:-Xms512m -Xmx512m,負載較高的可設置爲-Xms800m -Xmx800m

2C4G容器:-Xms2048m -Xmx2048m

4C8G容器:-Xms4096m -Xmx4096m

2、GC及編譯線程配置

-XX:ParallelGCThreads=[cpu核數]

注意:如果ParallelGCThreads 沒有配置,默認會使用物理機的cpu核數設置回收線程數,這會嚴重降低GC性能且產生過多GC線程,因此所有容器都應配置該參數;

-XX:CICompilerCount=[cpu核數 >2 ? cpu核數 : 2]

可以優化類文件編譯性能,默認會使用物理機cpu核數進行設置,所有容器都應該手動配置;

3、元空間

-XX:MetaspaceSize 元空間;
-XX:MaxMetaspaceSize 最大元空間;
元空間對應以前的永久代內存,推薦設置爲256~512M;
注意:MaxPermSize參數在JDK8中已經廢棄,應使用MetaspaceSize 進行設置

4、回收器類型

-XX:+UseParallelGC 並行回收器(默認)
-XX:+UseConcMarkSweepGC CMS回收器
-XX:+UseG1GC G1回收器
Jdk1.8的容器推薦使用G1,舊版本jdk容器推薦使用CMS,這兩個回收器都是響應時間優先的算法,對響應時間敏感的應用推薦使用;

GC日誌

當GC有問題時,可以打印GC日誌幫助排查,相關參數有:
-Xloggc:/export/Logs/jvm/gc.log
-XX:GCLogFileSize=100M
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+ PrintGCCause
-XX:+PrintClassHistogramBeforeFullGC
-XX:+PrintClassHistogramAfterFullGC
-XX:+HeapDumpBeforeFullGC
-XX:+HeapDumpAfterFullGC

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章