JVM GC一次調優實戰

CMS的Full GC採用壓縮式垃圾收集,在堆比較大的時候,若是full gc頻繁,會致使停頓,而且調用方阻塞、超時、甚至雪崩的狀況出現,因此下降full gc的發生頻率和須要時間,很是有必要。html

目標

  • 減小full gc頻率
  • 減小ygc和full gc時間

優化

配置變化

優化前java

<jvm-arg>-Xmx13312m</jvm-arg>
<jvm-arg>-Xms9216m</jvm-arg>
<jvm-arg>-Xmn1024m</jvm-arg>
<jvm-arg>-XX:PermSize=512M</jvm-arg>
<!-- 使用cms回收策略 -->
<jvm-arg>-XX:+UseParNewGC</jvm-arg>
<jvm-arg>-XX:+UseConcMarkSweepGC</jvm-arg>
<!-- 打印gc日誌 -->
<jvm-arg>-XX:+PrintGCDetails</jvm-arg>
<jvm-arg>-XX:+PrintGCDateStamps</jvm-arg>
<jvm-arg>-XX:+PrintHeapAtGC</jvm-arg>
<jvm-arg>-Xloggc:log/gc.log</jvm-arg>

優化後:windows

<jvm-arg>-Xmx13312m</jvm-arg>
<jvm-arg>-Xms13312m</jvm-arg>
<jvm-arg>-Xmn5120m</jvm-arg>
<jvm-arg>-XX:PermSize=512M</jvm-arg>
<jvm-arg>-XX:+UseParNewGC</jvm-arg>
<jvm-arg>-XX:+UseConcMarkSweepGC</jvm-arg>
<!-- 每次full gc以後,進行壓縮 -->
<jvm-arg>-XX:CMSFullGCsBeforeCompaction=0</jvm-arg>
<!-- 老年代佔用75%進行full gc -->
<jvm-arg>-XX:CMSInitiatingOccupancyFraction=75</jvm-arg>
<jvm-arg>-XX:CMSMaxAbortablePrecleanTime=30000</jvm-arg>
<jvm-arg>-XX:SurvivorRatio=6</jvm-arg>
<jvm-arg>-XX:+PrintGCDetails</jvm-arg>
<jvm-arg>-XX:+PrintGCDateStamps</jvm-arg>
<jvm-arg>-XX:+PrintHeapAtGC</jvm-arg>
<jvm-arg>-Xloggc:log/gc.log</jvm-arg>

第一次優化

將Xmx和Xms設置爲同樣大小,調大Xmnoracle

緣由jvm

  • 將Xmx和Xms設置爲同樣大小,沒有必要達到Xms後,再申請Xmx
  • Xmn是新生代的大小,直接影響着full gc次數,若是新生代比較小,那麼新生代的對象回收的就比較頻繁,年齡增加很快,因此老年代很快就滿了,從而致使full gc,官網建議新生代大小爲整個堆的:1/4~1/2,出處: https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

調整如下參數優化

<jvm-arg>-Xmx13312m</jvm-arg>
<jvm-arg>-Xms13312m</jvm-arg>
<jvm-arg>-Xmn4096m</jvm-arg>

效果 ygc由每分鐘40次,下降爲10次上下 fgc時間,由以前的100ms升爲300ms日誌

第二次優化

時間一長,發現單次full gc時間太長,有時候長達2s,日誌中發現pre clean沒有結束,就進行了remark致使的。code

CMS: abort preclean due to time 2016-06-13T08:58:07.672+0800: 63803.570: [CMS-concurrent-abortable-preclean: 5.190/5.211 secs] [Times: user=12.15 sys=1.38, real=5.21 secs] 
2016-06-13T08:58:07.677+0800: 63803.575: [GC[YG occupancy: 2652667 K (3774912 K)]63803.576: [Rescan (parallel) , 1.9969650 secs]63805.573: [weak refs processing, 0.0280880 secs] [1 CMS-remark: 7082120K(9437184K)] 9734787K(13212096K), 2.
0288350 secs] [Times: user=25.39 sys=0.11, real=2.03 secs]

pre clean用於保證remark的順利進行,若是pre clean階段沒有結束,就remark,會致使remark階段延長,pre clean默認是5s,延長至30s。 調整如下參數:htm

-XX:CMSMaxAbortablePrecleanTime=30000

效果 優化後,日發gc日誌中沒有abort preclean的現象發生了。對象

第三次優化

前兩次優化已經見到效果了,可是full gc仍是比較多,且持續時間比較長,增長配置,打印對象年齡:

-XX:+PrintTenuringDistribution

由於經過ygc的對象進入老年代,是按照年齡計算的,這個年齡默認是15,可是是動態調整的,因此加這個參數,再觀察一下。 查看日誌能夠看出,對象動態調全年齡是4,過低了,對象才4歲,就進入老年代了。

2016-06-14T19:51:31.639+0800: 770.958: [GC 770.959: [ParNew
Desired survivor size 214728704 bytes, new threshold 4 (max 4)
- age   1:   27368432 bytes,   27368432 total
- age   2:   12108344 bytes,   39476776 total
- age   3:   11933416 bytes,   51410192 total
- age   4:    5568312 bytes,   56978504 total

默認配置的15,怎麼才4就進入老年代了,原來jvm是動態對象年齡斷定的。

爲了能更好地適應不一樣程序的內存情況,虛擬機並不老是要求對象的年齡必須達到Max Tenuring Threshold才能晉升老年代

若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到Max Tenuring Threshold中要求的年齡。

既然取決於survivor的大小,調大survivor

修改配置:

-Xmn5120m
-XX:SurvivorRatio=6

調整後,age已經到15了。

2016-06-15T10:11:28.921+0800: 631.926: [GC 631.927: [ParNew
Desired survivor size 335544320 bytes, new threshold 15 (max 15)
- age   1:   44556600 bytes,   44556600 total
- age   2:   41122176 bytes,   85678776 total
- age   3:   14339240 bytes,  100018016 total
- age   4:   19359008 bytes,  119377024 total
- age   5:   13662592 bytes,  133039616 total
- age   6:   21144880 bytes,  154184496 total
- age   7:   17551328 bytes,  171735824 total
- age   8:    5390312 bytes,  177126136 total
- age   9:    8580176 bytes,  185706312 total
- age  10:    7921328 bytes,  193627640 total
- age  11:   17954784 bytes,  211582424 total
- age  12:    6197064 bytes,  217779488 total
- age  13:    7211632 bytes,  224991120 total
- age  14:   10837872 bytes,  235828992 total
- age  15:   10255800 bytes,  246084792 total
: 4236533K->288272K(4587520K), 0.0551150 secs] 4916941K->973217K(12976128K), 0.0557070 secs] [Times: user=0.80 sys=0.01, real=0.06 secs] 
Heap after GC invocations=68 (full 1):

效果

  • full gc由高峯期平均10分鐘一次,減小到平均1小時一次
  • ygc 由高峯期平均每分鐘40次,減小爲平均每分鐘8次
  • ygc由高峯期耗時由1.5s,減小爲平均500ms
  • 非高峯期,幾乎沒有full gc
  • full gc高峯期耗時有所增加,由原來的不到100ms,增加爲400ms,調整堆大小了嘛

輸入圖片說明

參考資料

相關文章
相關標籤/搜索