1.準備工作:
這段時間一直在看jvm調優的內容,苦于沒有環境讓自己練手,周三下午測試人員說測試后臺過于慢,之前一直以為是數據庫壓力太大的問題,后來連上測試環境,發現xmn=150m,新生代只分配了150m,而整個堆最大為2g,然后和運維同事溝通了,建議設置成1G,項目再打包,后臺頁面基本都秒開,雖然是個測試環境小小的調優,還是比較開心的哈.
測試環境畢竟是測試環境,因為開發沒有權限,有東西想改還是要和運維溝通,這幾天拿idea試試手,把idea調優下.先說下我的配置,E5 2660 ,8核16線程,16G內存,120G ssd,cpu雖然線程數比較多,但是主頻比較低,只有2.2g,idea在我電腦上啟動大概需要20s左右.其實前幾天就著手做這個事情,但是一直沒有辦法統計出idea具體的啟動時間,周志明大佬在深入jvm一書中是用的eclipse調優,eclipse插件似乎把啟動時間寫入到全局變量中了,idea我找了半天沒找到,今天下午突然發現,其實idea在啟動的時候會把啟動信息寫入到日志中,我的位置是在C:\Users\Administrator.IntelliJIdea2018.1\system\log\idea.log,這里面記錄了idea啟動都做了哪些事情
image
然后我就以app initialzation took作為時間的標準,后面這個13745ms我不知道它咋算出來的,啟動時間到這個點輸出一個是11667這個時間才對,然后我就以11667作為idea的啟動時間來判斷.如果是用linux來截取這個時間的話awk命令就夠了,做兩次管道
grep 'App initialization took' idea.log | awk '{print $4}'|awk -F ']' '{print $1}'
我是windows上跑的這個,然后就開了個pycharm,用python來處理這個時間,為了能夠在idea啟動的瞬間,就記錄這些信息,我把idea配置在了環境變量里,然后用python啟動,就是下面的openIdeaAndTools方法,這里順便吐槽下pycharm,我配完環境變量,idea死活起不來,然后電腦重啟下突然就好了,估計它的terminal緩存了系統的環境變量,不知道算不算bug.
#!/usr/bin/env python
# -*- coding=utf-8 -*-
import fileinput
import os
import threading
import time
class MyThread(threading.Thread):
def __init__(self, arg):
super(MyThread, self).__init__()
self.arg = arg
def run(self):
os.system(self.arg)
def getIdeaStartTime(logPath):
# 獲取idea的啟動時間
for line in fileinput.input(logPath):
if 'App initialization took' in line:
print(line.split('[')[-1].split(']')[0])
def openIdeaAndTools():
# 這里調用命令是阻塞執行,所以開啟線程異步執行
t1 = MyThread('idea64.exe ')
t1.start()
# 防止idea未啟動先執行了下面的代碼
time.sleep(0.1)
try:
# 調用jps命令
pid = os.popen("jps")
for l in pid:
# 因為idea啟動后,jps輸出的只有一個vmid,后面沒有類名,以此來判斷該進程是idea的
id = l.split(' ')[0]
info = l.split(' ')[1]
# 我這臺機器上還跑了pycharm,pycharm后面同樣沒有類名,就把pycharm的vmid去掉
if not info.strip() and int(id) != 1200:
print("idea的Id為" + id)
# 調用jstat
t4 = MyThread('jstat -gcutil ' + id + ' 1000 3')
t4.start()
# 調用jconsole
t2 = MyThread('jconsole ' + id)
# 調用jvisualvm
t3 = MyThread('jvisualvm')
t3.start()
t2.start()
finally:
pid.close()
if __name__ == "__main__":
# getIdeaStartTime(logPath = "C:\\Users\\Administrator\\.IntelliJIdea2018.1\\system\\log\\idea.log")
openIdeaAndTools()
2.未優化前:
首先說下idea的默認配置,在help-edit custom vm options可以看到,配置如下:
-Xms128m //最小堆128
-Xmx750m //最大750
-XX:ReservedCodeCacheSize=240m //代碼緩存
-XX:+UseConcMarkSweepGC //使用parnew+cms垃圾回收
-XX:SoftRefLRUPolicyMSPerMB=50 //和soft引用有關
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError //內存溢出dump出快照
-XX:-OmitStackTraceInFastThrow //拋出堆棧異常
整個的設置還是比較少的,經過多次測試,idea的啟動時間大概在18s左右,jstat信息如下(只截取了最后一次測試的前18s)
idea的Id為4440
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 100.00 63.01 1.64 94.90 83.95 1 0.011 0 0.000 0.011
0.00 100.00 61.75 16.45 95.42 83.95 3 0.067 0 0.000 0.067
100.00 0.00 28.84 22.12 94.73 83.02 4 0.106 0 0.000 0.106
100.00 0.00 68.97 22.12 94.73 83.02 4 0.106 0 0.000 0.106
0.00 100.00 30.26 31.35 96.83 93.83 5 0.117 1 0.005 0.122
100.00 0.00 0.00 35.89 97.20 94.96 6 0.131 2 0.027 0.158
90.56 0.00 10.54 38.82 97.53 95.00 8 0.141 2 0.027 0.168
100.00 0.00 68.57 46.48 97.53 95.04 10 0.165 2 0.027 0.192
0.00 97.85 22.65 51.15 97.05 92.71 11 0.175 2 0.027 0.202
0.00 100.00 73.74 60.28 97.18 94.92 13 0.225 2 0.027 0.252
0.00 100.00 91.61 60.28 97.18 94.92 13 0.225 2 0.027 0.252
100.00 0.00 4.12 64.82 97.41 93.94 14 0.235 2 0.027 0.262
100.00 0.00 12.22 64.82 97.41 93.94 14 0.235 2 0.027 0.262
100.00 0.00 71.19 64.82 97.41 93.94 14 0.235 2 0.027 0.262
0.00 100.00 43.52 78.72 96.97 92.43 17 0.315 3 0.029 0.343
0.00 100.00 6.02 74.14 95.99 91.92 19 0.341 5 0.053 0.395
100.00 0.00 35.30 67.15 95.24 90.23 20 0.370 6 0.092 0.462
100.00 0.00 35.64 78.42 94.32 87.14 22 0.419 7 0.094 0.512
這18s中可以看出YGC(也就是minor gc)發生了22次,共耗時0.419s,而FGC總共發生了7次,總耗時0.094s,ygc平均每次時間為0.019s,而fgc每次為0.013.
image
從這張圖中我們可以看出整個堆的大小先是平穩,后隨著使用堆大小的增加逐步變大,而使用堆頻繁gc,gc日志如下(前23s,比較長,就直接放服務器了):GC日志:
在0.942s的時候第一次出現了Allocation Failure,分配內存失敗,這會導致新生代觸發gc,這主要的原因是因為初始堆的大小設置成了128m.好了,有了解決思路,第一步要做的就是先禁掉插件,哈哈,人生就是需要皮一下,idea的log反應出插件的加載似乎是占用了很長一段時間,為了達到優化的效果,先把插件禁掉,在來對gc進行調優.
3.開工:
3.1禁插件
禁掉插件的效果非常明顯,測試了三次,時間分別為9296,10034,10363,而最后一次jstat的信息如下:
idea的Id為6512
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 100.00 91.18 1.63 94.81 83.95 2 0.028 0 0.000 0.028
0.00 100.00 49.32 16.38 95.35 83.95 3 0.068 0 0.000 0.068
0.00 100.00 50.65 16.38 95.35 83.95 3 0.068 0 0.000 0.068
100.00 0.00 29.37 21.33 94.72 85.34 4 0.089 0 0.000 0.089
100.00 0.00 49.98 21.33 94.72 85.34 4 0.089 0 0.000 0.089
100.00 0.00 90.62 21.33 94.72 85.34 4 0.089 1 0.009 0.099
0.00 100.00 45.10 32.07 97.56 94.05 5 0.112 1 0.009 0.121
100.00 0.00 79.42 36.54 97.22 95.07 6 0.128 2 0.035 0.163
90.65 0.00 92.93 39.97 97.63 95.09 9 0.169 2 0.035 0.204
100.00 0.00 62.49 47.90 97.69 95.15 10 0.195 2 0.035 0.230
10s內發生了10次ygc,2次fgc,其實說實話,這樣的性能完全很好了,但是本著這是一篇調優的文章,有點參數還是要改改,看看效果,首先調整的就是整個堆的大小.
3.2調整堆的大小
啟動的時候頻繁出現了allocation failure,這是因為堆的初始大小過小,導致新生代的內存過小,所以,先設置了以下參數:
-Xms2048m
-Xmx2048m
-Xmn1600m
堆的大小不在動態調整,而是固定在了2g,而新生代則直接分配了1600m,這一次,啟動時間為7s,jstat信息如下,前12s:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 16.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 24.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 32.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 36.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101
0.00 0.00 46.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101
0.00 0.00 54.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101
0.00 0.00 66.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101
0.00 0.00 70.00 0.00 17.24 19.25 0 0.000 1 0.101 0.101
0.00 0.00 78.00 0.00 17.24 19.25 0 0.000 2 0.101 0.101
0.00 0.00 90.00 0.00 17.24 19.25 0 0.000 2 0.258 0.258
0.00 0.00 96.00 0.00 17.24 19.25 0 0.000 2 0.258 0.258
0.00 41.29 20.32 0.00 96.90 94.00 1 0.054 3 0.267 0.321
一直到12s的時候,新生代才出現首次gc,gc日志異常的少,前面一直是cms在做初始標記-并發標記等工作,是在處理老年代內存,前7s只出現了一次老年代的回收,這也說明了大內存對程序的運行幫助那是相當大.
4.246: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(458752K)] 471859K(1933312K), 0.1010486 secs] [Times: user=0.16 sys=0.00, real=0.10 secs]
4.347: [CMS-concurrent-mark-start]
4.347: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
4.347: [CMS-concurrent-preclean-start]
4.349: [CMS-concurrent-preclean: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
4.349: [CMS-concurrent-abortable-preclean-start]
CMS: abort preclean due to time 9.362: [CMS-concurrent-abortable-preclean: 0.256/5.013 secs] [Times: user=13.81 sys=2.18, real=5.01 secs]
9.362: [GC (CMS Final Remark) [YG occupancy: 1022364 K (1474560 K)]9.362: [Rescan (parallel) , 0.1397593 secs]9.502: [weak refs processing, 0.0000532 secs]9.502: [class unloading, 0.0081261 secs]9.510: [scrub symbol table, 0.0068646 secs]9.517: [scrub string table, 0.0007781 secs][1 CMS-remark: 0K(458752K)] 1022364K(1933312K), 0.1575112 secs] [Times: user=1.06 sys=0.00, real=0.16 secs]
9.520: [CMS-concurrent-sweep-start]
9.520: [CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
9.520: [CMS-concurrent-reset-start]
9.523: [CMS-concurrent-reset: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
12.028: [GC (Allocation Failure) 12.028: [ParNew: 1310720K->67643K(1474560K), 0.0544422 secs] 1310720K->67643K(1933312K), 0.0545925 secs] [Times: user=0.51 sys=0.06, real=0.06 secs]
12.083: [GC (CMS Initial Mark) [1 CMS-initial-mark: 0K(458752K)] 106973K(1933312K), 0.0083509 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
12.091: [CMS-concurrent-mark-start]
12.091: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
12.091: [CMS-concurrent-preclean-start]
12.094: [CMS-concurrent-preclean: 0.003/0.003 secs] [Times: user=0.16 sys=0.01, real=0.00 secs]
12.095: [CMS-concurrent-abortable-preclean-start]
16.748: [CMS-concurrent-abortable-preclean: 4.303/4.653 secs] [Times: user=32.20 sys=1.31, real=4.65 secs]
16.748: [GC (CMS Final Remark) [YG occupancy: 737368 K (1474560 K)]16.748: [Rescan (parallel) , 0.0331714 secs]16.782: [weak refs processing, 0.0000574 secs]16.782: [class unloading, 0.0177730 secs]16.800: [scrub symbol table, 0.0229479 secs]16.823: [scrub string table, 0.0011784 secs][1 CMS-remark: 0K(458752K)] 737368K(1933312K), 0.0792557 secs] [Times: user=0.48 sys=0.00, real=0.08 secs]
16.828: [CMS-concurrent-sweep-start]
16.828: [CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
16.828: [CMS-concurrent-reset-start]
16.829: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
21.380: [GC (Allocation Failure) 21.380: [ParNew: 1378363K->124879K(1474560K), 0.0719474 secs] 1378363K->124879K(1933312K), 0.0721093 secs] [Times: user=0.53 sys=0.08, real=0.07 secs]
23.844: [GC (Allocation Failure) 23.845: [ParNew: 1435599K->78246K(1474560K), 0.2064758 secs] 1435599K->139746K(1933312K), 0.2066017 secs] [Times: user=1.06 sys=0.03, real=0.21 secs]
3.2 新生代調優
為了能夠說明某些問題,我把參數又調回了初始參數,因為如果用上面的參數繼續跑,基本沒法調了,用原始參數對新生代進行調優,在此首先需要介紹下parnew收集器.
3.2.1 parnew介紹
parnew是作為新生代的垃圾回收器,當然也就采用了復制的算法,首先進行可達性分析,stop the world,然后再進行垃圾回收,將eden和s0的數據復制到s1當中去,它和serial的區別就在于parnew是多線的,然后關于parnew的文檔,很可惜的是我在oracle官網上沒有找到,全是介紹g1,cms和parallel scavenge的,但是官網給出的常見問題列表中有個問題比較有意思,地址, What is the Parallel Young Generation collector (-XX:+UseParNewGC)?官網的回復是parNew和parallel表現出來的東西很像,但是在內部實現上不一樣,也就是說關于運行的機制大都差不多,畢竟都是并行的,但是在代碼實現上差很多,還有一個不同點就是parnew是能和cms搭配使用的,而parallel不行.
3.2.2調優參數
然后我就用了初始的vm參數中又加了幾個參數,(jvm的參數值介紹,官網地址)
第一個參數是-Xmn400m,分配給新生代400m的內存,另外固定住堆的大小為750M,防止因堆的大小不夠用而頻繁gc.
第二個是-XX:ParallelGCThreads,這個參數用于調整線程數,在我機器上默認是13個
image
第三個是-XX:+ScavengeBeforeFullGC,minor gc優先于fgc,這樣的好處就是往往會存在著新生代的對象持有老年代的引用(所以老年代的gc,通常會伴隨著fgc),這樣在fgc之前先清理掉一部分年輕代對象,會減少可達性分析的成本(在本次實驗中,似乎沒有展示出優勢,fgc次數比較少).
-Xms750m
-Xmx750m
-Xmn400m
-XX:ParallelGCThreads=20
-XX:+ScavengeBeforeFullGC
3.2.3調優結果
這一次啟動時間9s,然后看下前9s的jstat信息:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 28.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 50.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 52.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 60.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 64.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 68.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 76.00 0.00 17.24 19.25 0 0.000 0 0.000 0.000
0.00 0.00 84.00 0.00 17.24 19.25 0 0.000 1 0.089 0.089
0.00 66.86 5.20 0.00 96.69 92.35 1 0.025 1 0.089 0.115
0.00 66.86 33.40 0.00 96.69 92.35 1 0.025 1 0.089 0.115
由上面結果可得知,9s中共發生1次ygc,這我覺得益于-Xms750m,-Xmx750m,-Xmn400m,將新生代的內存大小固定在了400m,而減少了頻繁的gc.
3.2.4parnew gc日志解讀
然后這里順便解讀下gc的日志,關于parnew的:
9.399: [GC (Allocation Failure) 9.399: [ParNew: 327680K->27386K(368640K), 0.0253901 secs] 327680K->27386K(727040K), 0.0255930 secs] [Times: user=0.06 sys=0.06, real=0.03 secs]
9.399代表項目啟動時間,Allocation Failure代表gc的原因,因為內存分配失敗,ParNew代表使用的ParNew算法,327680K->27386K(368640K)代表,新生代回收前->新生代回收后(整個新生代大小),327680K->27386K(727040K)代表,整個堆回收前->整個堆回收后(整個堆的大小),0.0255930s代表此次垃圾回收的時間,[Times: user=0.06 sys=0.06, real=0.03 secs] 代表用戶線程執行的時間,系統線程執行的時間,總共耗時,因為用戶線程和系統線程并行執行,所以總耗時往往比user+sys時間要小.
3.3 老年代調優
3.3.1 cms介紹
老年代主要用的是cms垃圾回收器,使用的是標記-清除算法,正如之前所說的,major gc時因為存在老年代持有新生代的引用,所以會掃描整個堆而發生fgc,另外,因為標記-清除算法會產生碎片,當老年代的內存足夠分配一個對象,卻因內存不連續時,這時往往也會發生fgc,下面就簡單介紹下cms文章原文地址,CMS官方介紹
cms整個工作過程總共分為這幾個步驟:
1.初始標記,這一階段主要是通過gc root進行可達性分析,找到直接關聯的對象,這是需要stw.
2.并發標記,由第一階段標記出的gc信息出發,追蹤整個堆的對象信息,將所有活著的對象進行標記,因為是并發執行,所以沒有太多的開銷
3.并發預清理,此階段主要是為了減少重標記的時間,標記新生代晉升的對象,新分配到老年代的對象以及在并發階段被修改的對象,重新標記需要全量掃描整個堆大小(存在年輕代關聯老年代的引用,所以需要掃描整個堆),但是如果在并發預清理之后,重標記之前做一次minor gc(也就是類似于之前ScavengeBeforeFullGC)的功能,則能降低重標記的時間.所以它有個子階段.
????3.1 可中斷的并發預清理,這一階段主要的工作就是能夠在remark階段之前產生一次minor gc,由兩個參數控制CMSScheduleRemarkEdenSizeThreshold,CMSScheduleRemarkEdenPenetration,默認值分別是2M,50%,第一個代表當預清理完成之后,eden區域大于這個值時,開啟可中斷的預清理,直到eden空間使用率達到50%則中斷,并發預清理期間執行時長 由CMSMaxAbortablePrecleanTime = 5s 這個參數控制,意為并發預清理執行時長如果有5s了,那無論有無minor gc,有無達到50%,都停止并發預清理,進入重標記階段,如果沒有發生minor gc,則可由CMSScavengeBeforeRemark參數,使并發預清理后,remark之前強制執行一次minor gc(這會造成重標記時間減短,但minor gc是需要stw.所以使用還是要權衡)
4.重標記:會掃描新生代和老年代,日志中提現為Rescan(多線程),這個因為前面做了很多工作,所以停頓時間較少
5.并發清理:并發清理無效對象
6.重置:cms清除內部狀態.為下次回收準備
整個流程大致如此,這其中有幾個點需要說明下:
cms是如何識別老年代對象引用的?這里需要介紹下card table,card table其實就是一個數組,每個位置存儲的是一個byte,cms將老年代空間分為512bytes的快,card table每個元素對應一個塊,并發標記時.如果某個對象的引用發生了變化,就標記該對象所在的快為 dirty card,并發預清理會重新掃描該塊.將該對象引用的對象標記為可達,如圖所示:
初始狀態:
image
隨后老年代引用發生變化,對應的塊變為dirty card:
image
緊接著并發預清理,重新掃描修改后的引用,將其設置為可達:
image
對于老年代持有新生代的引用,minor gc是如何識別的?當有老年代引用新生代,對應的card table 被標識為相應的中(就是card table里面byte的8位,約定好每一位的含義,以此來區分)所以minor gc通過掃描card table就可以識別老年代引用新生代.
何為浮動垃圾?當并發預清理之時,用戶線程還在跑著,此時產生的垃圾只能下次回收,這一部分垃圾叫浮動垃圾.CMSInitiatingOccupancyFraction=n這個參數控制著老年代使用的比例達到n時,才開始垃圾回收,設置過低會造成老年代頻繁的垃圾回收,設置過高會導致無法分配內存空間,會出現Concurrent Mode Failure,從而導致cms使用備份serial old算法清除老年代,從而產生更長的stw.
何為動態檢查機制?UseCMSInitiatingOccupancyOnly,cms根據歷史,預測老年代需要多久填滿以及進行一次回收,老年代使用完之前,cms會根據預測進行gc.
cms如何碎片整理?UseCMSCompactAtFullCollection這個參數,就是進行fgc時,進行碎片整理,但是會造成stw時間變長,CMSFullGCsBeforeCompaction這個參數就是用于設置多少次不壓縮的fgc,緊接著來一次碎片整理.
以上算是對cms的一個總結,cms主要是為了解決兩個問題,一個是減少stw時間,另一個是減少空間碎片,所以性能性對于其他的一些老年代垃圾回收還是比較優秀的.
3.3.2 cms調優
不知道大家注意到沒有,第一次jstat信息老年代的使用空間幾乎沒有怎么使用,但是依舊是發生了fgc,用jstat -gccause可以看到gc原因,如下:
image
我們可以看到出現了Meradata GC threshold,jdk1.8取消了永久代,而是用堆外內存meta space來存放這些信息,而meta回收的及其繁瑣,尤其是類信息,需要判斷整個堆當中是否還有再用的該類的實例,所以這一階段觸發了fgc,而idea默認的MetaspaceSize大小為21M,所以此次對于老年代的調優,先是調大MetaspaceSize=128大小.
3.3.3 調優結果
這一次的效果非常明顯,一直到35s才第一次出現fgc,這一次idea的啟動時間還是11s,但是從gc信息中,我還是比較滿意的,這11s當中只出現了一次ygc,沒有出現fgc.
image
3.3.4 cms日志解讀
這邊主要是以如何分析cms日志為主,截取了一段gc日志:
4.407: [GC (CMS Initial Mark) [1 CMS-initial-mark: 19337K(87424K)] 50969K(126720K), 0.0049123 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
4.407s時初始標記開始,19337k(87424K)->老年代已使用(老年代大小) 50969K(126720K)->整個堆已使用(整個堆大小),0.0049123s共耗時多久,這一階段是需要stw的.
4.412: [CMS-concurrent-mark-start]
4.425: [CMS-concurrent-mark: 0.012/0.012 secs] [Times: user=0.11 sys=0.01, real=0.01 secs]
這一階段是并發標記階段,4.412并發標記開始的標志,4.425開始標記,0.012/0.012因為是并發執行,表示gc線程時間/用戶線程時間
4.425: [CMS-concurrent-preclean-start]
4.428: [CMS-concurrent-preclean: 0.003/0.004 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
4.428: [CMS-concurrent-abortable-preclean-start]**
4.658: [GC (Allocation Failure) 4.659: [ParNew: 39296K->4352K(39296K), 0.0110973 secs] 58633K->31757K(126720K), 0.0112153 secs] [Times: user=0.22 sys=0.00, real=0.01 secs]
5.741: [CMS-concurrent-abortable-preclean: 0.925/1.312 secs] [Times: user=4.96 sys=0.20, real=1.31 secs]
這是并發預清理階段,其中含有可中斷的并發預清理,用以讓年輕代發生minor gc,里面的信息和上面的類似,不在贅述.
5.741: [GC (CMS Final Remark) [YG occupancy: 23403 K (39296 K)]5.741: [Rescan (parallel) , 0.0075331 secs]5.749: [weak refs processing, 0.0000928 secs]5.749: [class unloading, 0.0064751 secs]5.755: [scrub symbol table, 0.0063506 secs]5.762: [scrub string table, 0.0010179 secs][1 CMS-remark: 27405K(87424K)] 50808K(126720K), 0.0224856 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
這是重標記階段,是需要stw的,掃描整個堆 YG occupancy: 23403 K (39296 K)表示年輕代當前使用的(年輕代總容量),rescan是其子階段,開始重新掃描,耗時0.0075331,weak refs processing,第二個子階段,處理弱引用,class unloading,第三個子階段,卸載未使用的類,scrub symbol table,第四份子階段,處理符號表,最后一個子階段scrub string table,處理string池?(后面兩個沒太明白,找了下也沒找到啥有用信息),CMS-remark: 27405K(87424K),在這個階段之后老年代占有的內存大小和老年代的容量;
5.764: [CMS-concurrent-sweep-start]
5.787: [CMS-concurrent-sweep: 0.023/0.023 secs] [Times: user=0.09 sys=0.02, real=0.02 secs]
并發清理階段
5.787: [CMS-concurrent-reset-start]
5.793: [CMS-concurrent-reset: 0.006/0.006 secs] [Times: user=0.08 sys=0.02, real=0.01 secs]
重置一些信息,以便下次垃圾回收.
4.最終的配置信息
-Xms750m
-Xmx750m
-Xmn400m
-XX:MetaspaceSize=128m
-XX:ParallelGCThreads=20
-XX:+ScavengeBeforeFullGC
-XX:ReservedCodeCacheSize=512m
-XX:+UseConcMarkSweepGC
-XX:SoftRefLRUPolicyMSPerMB=50
-XX:+PrintGCDetails
-Xloggc:d:\idea_gc.log
-XX:+PrintGCTimeStamps
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow