系統最常用的CMS GC mode——ParNew & CMS(Serial Old作爲替補)(heap> 5g)

工作中 常用的 CMS  GC模式  

refer to http://iamzhongyong.iteye.com/blog/1989829

如何讀懂GC日誌:

https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs



注: CMS GC下 會在特殊情況(jvm認爲內存不夠了concurrent mode failure,promotion fail) 轉而停下所有線程 去做full gc,也就是MSC(單線程)就是Serial Old(也算一次full gc)作爲替補,慢   參考 http://www.cnblogs.com/zuoxiaolong/p/jvm8.html

另外如果算一次 full gc ,實踐中 一次 CMS 中有兩次 stop the world 就算兩次 full gc了, 而MSC Serial Old作爲替補也算full gc, my god!  http://www.tuicool.com/articles/jq2yIza


這個是之前處理過的一個線上問題,處理過程斷斷續續,經歷了兩週多的時間,中間各種嘗試,總結如下。這篇文章分三部分:

1、問題的場景和處理過程;2、GC的一些理論東西;3、看懂GC的日誌

先說一下問題吧

問題場景:線上機器在半夜會推送一個700M左右的數據,這個時候有個數據置換的過程,也就是說有700M*2的數據在heap區域中,線上系統超時比較多,導致了很嚴重(嚴重程度就不說了)的問題。

問題原因:看日誌,系統接口超時的時候,系統出現了FullGC,這個時候stop-the-world了,也就停機了。分析gc的日誌,發現有promotion failed,根據FullGC觸發的條件,這個時候就會出現FullGC了。日誌如下:

1
2
2013-11-27T03:00:53.638+080035333.562: [GC 35333.562: [ParNew (promotion failed): 1877376K->1877376K(1877376K), 15.7989680 secs]35349.361: [CMS: 2144171K->2129287K(2146304K), 10.4200280 sec
s] 3514052K->2129287K(4023680K), [CMS Perm : 119979K->118652K(190132K)], 26.2193500 secs] [Times: user=30.35 sys=5.19, real=26.22 secs]

問題解決:中間調整過幾次,先搞了幾臺機器做了驗證,後來逐步推廣的。

1、調大heap區,由原來的4g,調整到5g,young區的大小不變,還是2g,這時候old區就由2g變爲3g了(這樣保證old區有足夠的空間);

2、設置-XX:UseCMSInitiatingOccupancyOnly,其實這個不關這個問題,只是發現半夜CMS進行的有點頻繁,就禁止掉了悲觀策略;

3、設置CMS區回收的比例,從80%調整到75%,讓old區儘早的進行,有足夠的空間剩餘;

 

爲什麼要有GC(垃圾回收)?

JVM通過GC來回收堆和方法區中的內存,GC的基本原理就是找到程序中不再被使用的對象,然後回收掉這些對象佔用的內存。

 

主要的收集器有哪些?

引用計數器和跟蹤計數器兩種。

引用計數器記錄對象是否被引用,當計數器爲零時,說明對象已經不再被使用,可以進行回收。java中的對象有複雜的引用關係,不是很適合引用計數器,所以sun jdk中並沒有實現這種GC方式。

跟蹤收集器,全局記錄數據的引用狀態,基於一定的條件觸發。執行的時候,從根集合開始掃描對象的引用關係,主要有複製(copying)、標記-清除(Mark-Sweep)、標記-壓縮(Mark-Compact)那種算法。

 

跟蹤計數器的三種算法簡介?

複製:從根集合搜掃描出存活的對象,然後將存活的對象複製到一塊新的未使用的空間中,當要回收的空間中存活的對象較少時,比較高效;

標記清除:從根集合開始掃描,對存活的對象進行標記,比較完畢後,再掃描整個空間中未標記的對象,然後進行回收,不需要對對象進行移動;

標記壓縮:標記形式和“標記清除”一樣,但是回收不存活的對象後,會把所有存活的對象在內存空間中進行移動,好處是減少了內存碎片,缺點是成本比較高;

 

java內存區域的形式是啥樣的?

這裏就不再介紹了,之前有一篇文章中專門介紹這個的(http://iamzhongyong.iteye.com/blog/1333100)。

 

新生代可用的GC?

新生代中對象存活的時間比較短,因此給予Copying算法實現,Eden區域存放新創建的對象,S0和S1區其中一塊用於存放在Minor GC的時候作爲複製存活對象的目標空間,另外一塊清空。

串行GC(Serial GC)比較適合單CPU的情況,可以通過-XX:UseSerialGC來強行制定;

並行回收GC(Parallel Scavenge),啓動的時候按照設置的參數來劃定Eden/S0/S1區域的大小,但是在運行時,會根據Minor GC的頻率、消耗時間來動態調整三個區域的大小,可以用過-XX:UseAdaptiveSizePolicy來固定大小,不進行動態調整;

並行GC(ParNew)劃分Eden、S1、S0的區域上和串行GC一樣。並行GC需要配合舊生代使用CMS GC(這是他和並行回收GC的不同)(如果配置了CMS GC的方式,那麼新生代默認採取的就是並行GC的方式);

 

啥時候會觸發Minor GC?

當Eden區域分配內存時,發現空間不足,JVM就會觸發Minor GC,程序中System.gc()也可以來觸發。

 

舊生代可用的GC方式有哪幾種?

串行GC(Serial MSC)、並行GC(Parallel MSC)、併發GC(CMS);

 

關於CMS?

採用CMS時候,新生代必須使用Serial GC或者ParNew GC兩種。CMS共有七個步驟,只有Initial Marking和Final Marking兩個階段是stop-the-world的(7phase  參考文章頂),其他步驟均和應用並行進行。持久代的GC也採用CMS,通過-XX:CMSPermGenSweepingEnabled -XX:CMSClassUnloadingEnabled來制定。在採用cms gc的情況下,ygc變慢的原因通常是由於old gen出現了大量的碎片。

 

爲啥CMS會有內存碎片,如何避免?

由於在CMS的回收步驟中,沒有對內存進行壓縮,所以會有內存碎片出現,CMS提供了一個整理碎片的功能,通過-XX:UseCompactAtFullCollection來啓動此功能,啓動這個功能後,默認每次執行Full GC的時候會進行整理(也可以通過-XX:CMSFullGCsBeforeCompaction=n來制定多少次Full GC之後來執行整理),整理碎片會stop-the-world.

 

啥時候會觸發CMS GC?

1、舊生代或者持久代已經使用的空間達到設定的百分比時(CMSInitiatingOccupancyFraction這個設置old區,perm區也可以設置);

2、JVM自動觸發(JVM的動態策略,也就是悲觀策略)(基於之前GC的頻率以及舊生代的增長趨勢來評估決定什麼時候開始執行),如果不希望JVM自行決定,可以通過-XX:UseCMSInitiatingOccupancyOnly=true來制定;

3、設置了 -XX:CMSClassUnloadingE考慮nabled 這個則考慮Perm區;

 

啥時候會觸發Full GC?

一、舊生代空間不足:java.lang.outOfMemoryError:java heap space;

二、Perm空間滿:java.lang.outOfMemoryError:PermGen space;

三、CMS GC時出現promotion failed  和concurrent  mode failure(Concurrent mode failure發生的原因一般是CMS正在進行,但是由於old區內存不足,需要儘快回收old區裏面的死的java對象,這個時候foreground gc需要被觸發,停止所有的java線程,同時終止CMS,直接進行MSC。);

四、統計得到的minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間;

五、主動觸發Full GC(執行jmap -histo:live [pid])來避免碎片問題;

 

爲啥heap小於3g不建議使用CMS GC這種方式?

http://hellojava.info/?p=142 畢大師的這篇文章講的很清楚。

1、觸發比例不好設置,設置大了,那麼剩餘的空間就少了很多,設置小了,那old區還沒放置多少東西,就要進行回收了;

2、CMS進行的時候,是並行的,也就意味着如果過於頻繁的話,會和應用的強佔CPU;

3、CMS會有內存 碎片問題;

4、YGC的速率變慢(由於CMS GC的實現原理,導致對象從新生代晉升到舊生代時,尋找哪裏能放下的這個步驟比ParallelOld GC是慢一些的,因此就導致了YGC速度會有一定程度的下降。);

 

JVM的悲觀策略是啥?

所謂的悲觀策略(http://tmalltesting.com/archives/663 我們性能測試團隊一個同學分析的案例),就是JVM不按照JVM指定的參數來進行CMS GC,而是根據內存情況以及之前回收的方式動態調整,自行進行GC。舊生代剩餘的空間(available)大於新生代中使用的空間(max_promotion_in_bytes),或者大於之前平均晉升的old的大小(av_promo),返回false。cms gc是每隔一個週期(默認2s)就會做一次這個檢查,如果爲false,則不執行YGC,而觸發cms gc。

 

我們經常使用的是啥GC方式?

針對目前線上機器的情況(8G的物流內存),heap區一般設置在4g或者5g左右,一般是使用CMS GC,這時候:

young區使用ParNew(並行GC),Old+Perm(需要單獨設置)使用CMS,整個堆(young+old+perm)使用MSC((Mark Sweep Compact)是CMS GC算法的Full GC算法,單線程回收整個堆,回收過程有嚴格的步驟。壓縮,所以回收完理論上任何Generation都不會有內存碎片)壓縮回收的方式。


讀懂GC日誌?

基本上都是這種格式:回收前區域佔用的大小->回收後區域佔用的大小(區域設置的大小),佔用的時間

 

1、promotion failed的一段日誌

1
2
2013-11-27T03:00:53.638+080035333.562: [GC 35333.562: [ParNew (promotion failed): 1877376K->1877376K(1877376K), 15.7989680 secs]35349.361: [CMS: 2144171K->2129287K(2146304K), 10.4200280 sec
s] 3514052K->2129287K(4023680K), [CMS Perm : 119979K->118652K(190132K)], 26.2193500 secs] [Times: user=30.35 sys=5.19, real=26.22 secs]

解釋如下:

1
2
3
4
5
1877376K->1877376K(1877376K), 15.7989680 secs   young區
2144171K->2129287K(2146304K), 10.4200280 sec     old區情況
3514052K->2129287K(4023680K)                     heap區情況
119979K->118652K(190132K)], 26.2193500 secs      perm區情況 
[Times: user=30.35 sys=5.19, real=26.22 secs]    整個過程的時間消耗

 

2、一段正常的CMS的日誌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2013-11-27T04:00:12.819+080038892.743: [GC [1 CMS-initial-mark: 1547313K(2146304K)] 1734957K(4023680K), 0.1390860 secs] [Times: user=0.14 sys=0.00, real=0.14 secs]
2013-11-27T04:00:12.958+080038892.883: [CMS-concurrent-mark-start]
2013-11-27T04:00:19.231+080038899.155: [CMS-concurrent-mark: 6.255/6.272 secs] [Times: user=8.49 sys=1.57, real=6.27 secs]
2013-11-27T04:00:19.231+080038899.155: [CMS-concurrent-preclean-start]
2013-11-27T04:00:19.250+080038899.175: [CMS-concurrent-preclean: 0.018/0.019 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
2013-11-27T04:00:19.250+080038899.175: [CMS-concurrent-abortable-preclean-start]
 CMS: abort preclean due to time 2013-11-27T04:00:25.252+080038905.176: [CMS-concurrent-abortable-preclean: 5.993/6.002 secs] [Times: user=6.97 sys=2.16, real=6.00 secs]
2013-11-27T04:00:25.253+080038905.177: [GC[YG occupancy: 573705 K (1877376 K)]38905.177: [Rescan (parallel) , 0.3685690 secs]38905.546: [weak refs processing, 0.0024100 secs]38905.548: [cla
ss unloading, 0.0177600 secs]38905.566: [scrub symbol & string tables, 0.0154090 secs] [1 CMS-remark: 1547313K(2146304K)] 2121018K(4023680K), 0.4229380 secs] [Times: user=1.41 sys=0.01, real=
0.43 secs]
2013-11-27T04:00:25.676+080038905.601: [CMS-concurrent-sweep-start]
2013-11-27T04:00:26.436+080038906.360: [CMS-concurrent-sweep: 0.759/0.760 secs] [Times: user=1.06 sys=0.48, real=0.76 secs]
2013-11-27T04:00:26.436+080038906.360: [CMS-concurrent-reset-start]
2013-11-27T04:00:26.441+080038906.365: [CMS-concurrent-reset: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

這個是一個正常的CMS的日誌,共分爲七個步驟,重點關注initial-mark和remark這兩個階段,因爲這兩個是停機的。

A、[GC [1 CMS-initial-mark: 1547313K(2146304K)] 1734957K(4023680K), 0.1390860 secs] [Times: user=0.14 sys=0.00, real=0.14 secs]

各個數據依次表示標記前後old區的所有對象佔內存大小和old的capacity,整個JavaHeap(不包括perm)所有對象佔內存總的大小和JavaHeap的capacity。

B、2013-11-27T04:00:25.253+0800: 38905.177: [GC[YG occupancy: 573705 K (1877376 K)]38905.177: [Rescan (parallel) , 0.3685690 secs]38905.546: [weak refs processing, 0.0024100 secs]38905.548: [class unloading, 0.0177600 secs]38905.566: [scrub symbol & string tables, 0.0154090 secs] [1 CMS-remark: 1547313K(2146304K)] 2121018K(4023680K), 0.4229380 secs] [Times: user=1.41 sys=0.01, real=0.43 secs]

Rescan (parallel)表示的是多線程處理young區和多線程掃描old+perm的卡表的總時間, parallel 表示多GC線程並行。

weak refs processing 處理old區的弱引用的總時間,用於回收native memory。

class unloading 回收SystemDictionary消耗的總時間。

 

3、一段正常的Young GC的日誌

1
2
2013-11-27T04:00:07.345+080038887.270: [GC 38887.270: [ParNew: 1791076K->170624K(1877376K), 0.2324440 secs] 2988366K->1413629K(4023680K), 0.2326470 secs] [Times: user=0.80 sys=0.00, real=0.
23 secs]

ParNew這個表明是並行的回收方式,具體的分別是young區、整個heap區的情況;

 

4、一段通過system.gc產生的FullGC日誌

1
2013-07-21T17:44:01.554+080050.568: [Full GC (System) 50.568: [CMS: 943772K->220K(2596864K), 2.3424070 secs] 1477000K->220K(4061184K), [CMS Perm : 3361K->3361K(98304K)], 2.3425410 secs] [Times: user=2.33 sys=0.01, real=2.34 secs]

解釋如下:

Full GC (System)意味着這是個system.gc調用產生的MSC。

“943772K->220K(2596864K), 2.3424070 secs”表示:這次MSC前後old區內總對象大小,old的capacity及這次MSC耗時。

“1477000K->220K(4061184K)”表示:這次MSC前後JavaHeap內總對象大小,JavaHeap的capacity。

“3361K->3361K(98304K)], 2.3425410 secs”表示:這次MSC前後Perm區內總對象大小,Perm區的capacity。

 

5、一個特殊的GC日誌,根據動態計算直接進行的FullGC(MSC的方式)

1
2013-03-13T13:48:06.349+08007.092: [GC 7.092: [ParNew: 471872K->471872K(471872K), 0.0000420 secs]7.092: [CMS: 366666K->524287K(524288K), 27.0023450 secs] 838538K->829914K(996160K), [CMS Perm : 3196K->3195K(131072K)], 27.0025170 secs]

ParNew的時間特別短,jvm在minor gc前會首先確認old是不是足夠大,如果不夠大,這次young gc直接返回,進行MSC。

(CMS 中full gc採用的MSC 是單線程的,所以特別慢,這裏有27s)

 

參考文章:

http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm   JVM參數大全

http://tmalltesting.com/archives/663 悲觀策略

http://hellojava.info/?p=142 CMS GC不建議3G的原因

畢玄的《分佈式java基礎》

如何讀懂GC日誌:

https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs

——————————

——————————————————————

實例

爲什麼CMS GC時出現Concurrent Mode Failure?


    併發收集器(concurrentcollector)指的是回收年老代和持久代時,採用多個線程和應用線程併發執行,減少應用停頓時間,但如果參數設置不當,容易出現Concurrent ModeFailure現象,此時JVM將採用停頓的方式進行full gc(就是parallel old 作爲替補,算full gc此時,cms 因爲7個階段中有兩次停頓算兩次),整個gc時間相當可觀,完全違背了採用CMS GC的初衷。

    出現此現象的原因主要有兩個:一個是在年老代被用完之前不能完成對無引用對象的回收;一個是當新空間分配請求在年老代的剩餘空間中得到滿足。原文如(if theconcurrent collector is unable to finish reclaiming the unreachable objectsbefore the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped)。

出現此現象的具體gc日誌如下:

90003.167: [GC 90003.167: [ParNew: 261760K->0K(261952K), 0.0204310secs] 778897K->520196K(1310528K), 0.0207190 secs]
90006.049: [GC 90006.050: [ParNew: 261760K->0K(261952K), 0.0136380 secs]781956K->521446K(1310528K), 0.0138720 secs]
90010.628: [GC 90010.628: [ParNew: 261760K->261760K(261952K), 0.0000350secs]90010.628: [CMS (concurrent mode failure)[Unloadingclass sun.reflect.GeneratedSerializationConstructorAccessor1818]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1816]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1819]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1821]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1817]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1822]
[Unloading class sun.reflect.GeneratedSerializationConstructorAccessor1820]
: 521446K->415777K(1048576K), 2.2550270 secs] 783206K->415777K(1310528K),2.2553820 secs]
90015.099: [GC 90015.099: [ParNew: 261760K->0K(261952K), 0.0198180 secs]677537K->418003K(1310528K), 0.0200650 secs]
90018.670: [GC 90018.670: [ParNew: 261760K->0K(261952K), 0.0131610 secs]679763K->419115K(1310528K), 0.0133750 secs]
90022.254: [GC 90022.254: [ParNew: 261760K->0K(261952K), 0.0151240 secs]680875K->420505K(1310528K), 0.0154180 secs]

當時的JVM參數如下:

-server -Xms1280m -Xmx1280m -Xmn256m -Xss256k -XX:PermSize=128m-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection  -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled-XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

因爲配置了+CMSClassUnloadingEnabled參數,所以出現Unloading classsun.reflect.GeneratedSerializationConstructorAccessor的日誌,這是個好習慣,如果空間不夠時可以卸載類來釋放空間,以進行FULL GC,相反,如果gc日誌中出現了此日誌,應該檢查各代的大小設置是否合理。這裏應用從啓動到上述現象出現時還沒有進行過CMS GC,出現concurrent modefailure現象的原因是年輕代GC(ParNew),年老代所剩下的空間不足以滿足年輕代,也就是開頭提到的原因二。要避免此現象,方法一是降低觸發CMS的閥值,即參數-XX:CMSInitiatingOccupancyFraction的值,默認值是68,所以這裏調低到50,讓CMS GC儘早執行,以保證有足夠的空間,如下:

-server -Xms1280m -Xmx1280m -Xmn256m -Xss256k -XX:PermSize=128m-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC-XX:CMSFullGCsBeforeCompaction=1 -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=50-XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc-XX:+PrintGCDetails -XX:+PrintGCTimeStamps

調完之後發現還是一樣的現象(這裏有點不是很明白,年老代空間爲1024m(1280m-256m),50%時觸發CMS GC,也就是在年老代512m的時候,剩下的堆空間有512m,就算年輕代全部裝進去應該也是夠的),所以懷疑是年輕代太大,有大的對象,在年老代有碎片的情況下將很難分配,所以有了第二個解決辦法,即減少年輕代大小,避免放入年老代時需要分配大的空間,同時調整full gc時壓縮碎片的頻次,減少持久代大小,以及將觸發CMS GC的閥值適當增大(因爲年輕代小了,這個調大點沒關係,後面可以再調試這個參數),參數如下:

-server -Xms1280m -Xmx1280m -Xmn128m -Xss256k -XX:PermSize=96m -XX:MaxPermSize=96m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=1 -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

調整完後沒有那個現象了,這裏主要起作用的就是調小年輕代大小。在年老代到達826m(觸發CMS閥值(1280-128)*0.7=806m)時出現了CMS GC,用時27ms,日誌如下:

705.444: [GC 705.445: [ParNew: 130944K->0K(131008K), 0.0197680 secs]954628K->826284K(1310656K), 0.0199720 secs]

705.467:[GC [1 CMS-initial-mark: 826284K(1179648K)] 826744K(1310656K), 0.0271540 secs]

705.494:[CMS-concurrent-mark-start]

706.717:[CMS-concurrent-mark: 1.223/1.223 secs]

706.717:[CMS-concurrent-preclean-start]

706.717:[CMS-concurrent-preclean: 0.000/0.000 secs]

706.742:[CMS-concurrent-abortable-preclean-start]

706.742:[CMS-concurrent-abortable-preclean: 0.000/0.000 secs]
707.067: [GC 707.067: [ParNew: 130944K->0K(131008K), 0.0160200 secs]957228K->827348K(1310656K), 0.0162110 secs]
707.796: [GC[YG occupancy: 66671 K (131008 K)]707.796: [Rescan (parallel) ,0.0278280 secs]707.824: [weak refs processing, 0.0420770 secs] [1 CMS-remark:827348K(1179648K)] 894019K(1310656K), 0.0711970 secs]
707.877: [CMS-concurrent-sweep-start]
708.453: [GC 708.454: [ParNew: 130944K->0K(131008K), 0.0203760 secs]848439K->718796K(1310656K), 0.0205780 secs]
709.833: [GC 709.833: [ParNew: 130944K->0K(131008K), 0.0160170 secs]430484K->301411K(1310656K), 0.0161840 secs]
709.916: [CMS-concurrent-sweep: 1.974/2.040 secs]
709.916: [CMS-concurrent-reset-start]
709.951: [CMS-concurrent-reset: 0.034/0.034 secs]
711.187: [GC 711.187: [ParNew: 130944K->0K(131008K), 0.0130890 secs]413136K->283326K(1310656K), 0.0132600 secs]

觀察一段時間的gc情況,gc效率也很高,單次YGCT<20ms,FGCT <40ms:

$ jstat -gcutil 31935 1000
  S0     S1     E      O     P     YGC     YGCT    FGC    FGCT    GCT

  0.00   0.00  64.29  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.33  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.41  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.45  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.49  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.58  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.63  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.69  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.72  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.75  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.79  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.84  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.90  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.95  36.47  73.15   1293  19.514     6    0.211   19.725
  0.00   0.00  64.99  36.47  73.15   1293  19.514     6    0.211   19.725

這時,想再測試下-XX:CMSInitiatingOccupancyFraction的值,調到80時又出現了上述現象Concurrent ModeFailure,啓動後還沒進行過CMS GC,在年老代914m時就出現了:

759.994: [GC 759.994: [ParNew: 130944K->0K(131008K), 0.0172910 secs]1040896K->911480K(1310656K), 0.0174730 secs]
760.879: [GC 760.879: [ParNew: 130944K->0K(131008K), 0.0300920 secs]1042424K->914190K(1310656K), 0.0302950 secs]
761.768: [GC 761.769: [ParNew: 130944K->130944K(131008K), 0.0000340secs]761.769: [CMS (concurrent mode failure)[Unloading classsun.reflect.GeneratedMethodAccessor342]edMethodAccessor348]
[Unloading class sun.reflect.GeneratedMethodAccessor411]
[Unloading class sun.reflect.GeneratedMethodAccessor407]
[Unloading class sun.reflect.GeneratedMethodAccessor541]

最後總結下,出現Concurrent ModeFailure現象時,解決辦法就是要讓年老代留有足夠的空間,以保證新對象空間的分配。另外在JVM  BUG中有提到,JDK1.5_09版本之前,JVM參數-XX:CMSInitiatingOccupancyFraction是無效的,我這裏應用環境的版本是JDK1.5_08,從gc日誌來看是可以生效的。

GC時還有一個常見的錯誤PromotionFailed,解決辦法類似,也是調整年輕代和年老代的比例,還有CMSGC的時機。


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