CMS之promotion failedconcurrent mode failure
CMS并行GC收集器是大多數JAVA服務應用的最佳選擇,然而, CMS并不是完美的,在使用CMS的過程中會產生2個最讓人頭痛的問題:
promotion failed
該問題是在進行Minor GC時,Survivor Space放不下,對象只能放入老年代,而此時老年代也放不下造成的。(promotion failed時老年代CMS還沒有機會進行回收,又放不下轉移到老年代的對象,因此會出現下一個問題concurrent mode failure,需要stop-the-wold 降級為GC-Serail Old)。
下面是一個promotion failed的一條gc日志:
concurrent mode failure
該問題是在執行CMS GC的過程中同時業務線程將對象放入老年代,而此時老年代空間不足,或者在做Minor GC的時候,新生代Survivor空間放不下,需要放入老年代,而老年代也放不下而產生的。
下面是一個concurrent mode failure的一條gc日志:
下面我們詳細的分析這兩個問題產生的原因以及如何進行解決。
首先我們經常遇到promotion failed問題,這也確實是個很頭痛的問題,一般是進行Minor GC的時候,發現Survivor空間不夠,所以,需要移動一些新生帶的對象到老年帶,然而,有些時候盡管老年代有足夠的空間,但是由于CMS采用標記清除算法,默認并不使用標記整理算法,可能會產生很多碎片,因此,這些碎片無法完成大對象向老年帶轉移,因此需要進行CMS在老年帶的Full GC來合并碎片。
這個問題的直接影響就是它會導致提前進行CMS Full GC, 盡管這個時候CMS的老年代并沒有填滿,只不過有過多的碎片而已,但是Full GC導致的stop-the-wold是難以接受的。
解決這個問題的辦法就是可以讓CMS在進行一定次數的Full GC(標記清除)的時候進行一次標記整理算法,CMS提供了以下參數來控制:
-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5
也就是CMS在進行5次Full GC(標記清除)之后進行一次標記整理算法,從而可以控制老年代的碎片在一定的數量以內,甚至可以配置CMS在每次Full GC的時候都進行內存的整理。
另外,有些應用存在比較大的對象朝生熄滅,這些對象在救助空間無法容納,因此,會提早進入老年代,老年代如果有碎片,也會產生promotion failed, 因此我們應該控制這樣的對象在新生代,然后在下次Minor GC的時候就被回收掉,這樣避免了過早的進行CMS Full GC操作,下面的一個配置樣例就通過增加Survivor空間的大小來解決這個問題:
-Xmx4000M -Xms4000M -Xmn600M -XXmSize=500M -XX:MaxPermSize=500M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled eCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log
上面討論了promotion failed引起的原因以及解決方案,除了promotion failed還有一個情況會引起CMS回收失敗,從而退回到Serial Old收集器進行回收,我們在線上尤其要注意的是concurrent mode failure出現的頻率,這可以通過-XX:+PrintGCDetails來觀察,當出現concurrent mode failure的現象時,就意味著此時JVM將繼續采用Stop-The-World的方式來進行Full GC,這種情況下,CMS就沒什么意義了,造成concurrent mode failure的原因是當minor GC進行時,1)舊生代所剩下的空間小于Eden區域+From區域的空間,或者2)在CMS執行老年代的回收時有業務線程試圖將大的對象放入老年代,導致CMS在老年代的回收慢于業務對象對老年代內存的分配。
解決這個問題的通用方法是調低觸發CMS GC執行的閥值,CMS GC觸發主要由CMSInitiatingOccupancyFraction值決定,默認情況是當舊生代已用空間為68%時,即觸發CMS GC,在出現concurrent mode failure的情況下,可考慮調小這個值,提前CMS GC的觸發,以保證舊生代有足夠的空間。
總結:
Minor GC后, Survivor空間容納不了剩余對象,將要放入老年代,老年代有碎片或者不能容納這些對象,就產生了concurrent mode failure, 然后進行stop-the-world的Serial Old收集器。
解決辦法:-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5 或者調大新生代或者Survivor空間
CMS是和業務線程并發運行的,在執行CMS的過程中有業務對象需要在老年代直接分配,例如大對象,但是老年代沒有足夠的空間來分配,所以導致concurrent mode failure, 然后需要進行stop-the-world的Serial Old收集器。
解決辦法:+XX:CMSInitiatingOccupancyFraction,調大老年帶的空間,+XX:CMSMaxAbortablePrecleanTime
總結一句話:使用標記整理清除碎片和提早進行CMS操作。
【轉自】https://my.oschina.net/hosee/blog/674181
總結
以上是生活随笔為你收集整理的CMS之promotion failedconcurrent mode failure的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 知乎容器化构建系统设计和实践
- 下一篇: 技术 KPI 的量化