文档章节

使用CMS垃圾收集器产生的问题和解决方案

Hosee
 Hosee
发布于 2016/05/12 16:24
字数 1633
阅读 6.8K
收藏 5

关于CMS垃圾收集器,在JVM GC参数以及GC算法的应用中已经提到了。

CMS并行GC是大多数应用的最佳选择,然而, CMS并不是完美的,在使用CMS的过程中会产生2个最让人头痛的问题:

  1. promotion failed
  2. concurrent mode failure

第一个问题promotion failed是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的,多数是由于老年带有足够的空闲空间,但是由于碎片较多,这时如果新生代要转移到老年带的对象比较大,所以,必须尽可能提早触发老年代的CMS回收来避免这个问题(promotion failed时老年代CMS还没有机会进行回收,又放不下转移到老年带的对象,因此会出现下一个问题concurrent mode failure,需要stop-the-wold GC- Serail Old)。

下面是一个promotion failed的一条gc日志:

106.641: [GC 106.641: [ParNew (promotion failed): 14784K->14784K(14784K), 0.0370328 secs]106.678: [CMS106.715: [CMS-concurrent-mark: 0.065/0.103 secs] [Times: user=0.17 sys=0.00, real=0.11 secs]
(concurrent mode failure): 41568K->27787K(49152K), 0.2128504 secs] 52402K->27787K(63936K), [CMS Perm : 2086K->2086K(12288K)], 0.2499776 secs] [Times: user=0.28 sys=0.00, real=0.25 secs]

第二个问题concurrent mode failure是在执行CMS GC的过程中同时业务线程将对象放入老年代,而此时老年代空间不足,这时CMS还没有机会回收老年带产生的,或者在做Minor GC的时候,新生代救助空间放不下,需要放入老年代,而老年代也放不下而产生的。

尽管CMS使用一个叫做分配担保的机制,每次Minor GC之后要保证新生代的空间survivor + eden > 老年带的空闲时间,但是对象分配是不可预测的,总会有写对象分配在老年带是满足不了的。

下面是一个concurrent mode failure的一条gc日志:

0.195: [GC 0.195: [ParNew: 2986K->2986K(8128K), 0.0000083 secs]0.195: [CMS0.212: [CMS-concurrent-preclean: 0.011/0.031 secs] [Times: user=0.03 sys=0.02, real=0.03 secs]
(concurrent mode failure): 56046K->138K(57344K), 0.0271519 secs] 59032K->138K(65472K), [CMS Perm : 2079K->2078K(12288K)], 0.0273119 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]

下面我们详细的分析这两个问题产生的原因以及如何进行解决。

首先我们经常遇到promotion failed问题,这也确实是个很头痛的问题,一般是进行Minor GC的时候,发现救助空间不够,所以,需要移动一些新生带的对象到老年带,然而,有些时候尽管老年带有足够的空间,但是由于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操作,下面的一个配置样例就通过增加救助空间的大小来解决这个问题:

-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进行时,旧生代所剩下的空间小于Eden区域+From区域的空间,或者在CMS执行老年带的回收时有业务线程试图将大的对象放入老年带,导致CMS在老年带的回收慢于业务对象对老年带内存的分配。

解决这个问题的通用方法是调低触发CMS GC执行的阀值,CMS GC触发主要由CMSInitiatingOccupancyFraction值决定,默认情况是当旧生代已用空间为68%时,即触发CMS GC,在出现concurrent mode failure的情况下,可考虑调小这个值,提前CMS GC的触发,以保证旧生代有足够的空间。

总结:

1. promotion failed – concurrent mode failure

Minor GC后, 救助空间容纳不了剩余对象,将要放入老年带,老年带有碎片或者不能容纳这些对象,就产生了concurrent mode failure, 然后进行stop-the-world的Serial Old收集器。

解决办法:-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5 或者 调大新生代或者救助空间

2. concurrent mode failure

CMS是和业务线程并发运行的,在执行CMS的过程中有业务对象需要在老年带直接分配,例如大对象,但是老年带没有足够的空间来分配,所以导致concurrent mode failure, 然后需要进行stop-the-world的Serial Old收集器。

解决办法:+XX:CMSInitiatingOccupancyFraction,调大老年带的空间,+XX:CMSMaxAbortablePrecleanTime

总结一句话:使用标记整理清除碎片和提早进行CMS操作。

本文转载自:http://cloudate.net/?p=292

Hosee
粉丝 621
博文 135
码字总数 209956
作品 0
杭州
程序员
私信 提问
加载中

评论(5)

zehongzheng
zehongzheng
里面对 分配担保的 解释有问题. 正确的解释是 : HandlePromotionFailure jdk6后已废弃, 分配担保规则为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC
逝去的回忆
逝去的回忆
本身上面的理解就有问题,两个失败原因解释的完全就是错的额
叫我图图就可以了
刚好遇到了相同的问题
清风-蓝魔泪
清风-蓝魔泪
1. promotion failed -- 调大新生代或者救助空间
2. concurrent mode failure -- 调大老年带的空间
总感觉有些不太对啊,一直调大吗,空间过多,也会导致一次GC的时间更长啊
19492049
19492049
学习了
JVM学习之:GC的算法的具体实现(垃圾收集器)

在 JVM学习之:GC的算法简述 这篇博客中提到了各种各样的垃圾算法,由于java屏蔽掉了程序员对内存的管理,其根据这些算法的特点实现了很多的的垃圾收集器,每个垃圾收集器都有自己的优点,使用场合...

sun7545526
2012/08/15
0
0
二:JVM学习-垃圾收集算法以及常用的垃圾收集器

垃圾收集器与内存分配策略简介: 一:垃圾收集算法 1.1:简介 说起垃圾收集,大部分人都把这项技术当做java语言的伴生物,其实,GC的历史比java久远,1960年诞生于MIT的Lisp是第一门真正使用...

四月李
2016/02/20
306
0
【BAT面试题系列】Java面试必考题JVM的最完整详解,深度解析背后原理

JVM是BAT面试中的Java必考题目。 想要完美解答JVM相关的面试问题,至少需要掌握JVM的内存管理、JVM垃圾回收算法,及今天重点谈到的JVM垃圾收集器(即JVM垃圾回收算法的具体实现)。 Mike在本...

mikechen优知
2019/02/26
362
0
Java虚拟机四:垃圾回收算法与垃圾收集器

Java虚拟机四:垃圾回收算法与垃圾收集器 在Java运行时的几个数据区域中,程序计数器,虚拟机栈,本地方法栈3个区域随着线程而生,随线程而灭,因此这几个区域的内存分配和回收具有确定性,不...

优惠码发放
2019/05/01
0
0
JVM实用参数(七)CMS收集器

原文连接 转载连接 译者: iDestiny 校对:梁海舰 HotSpot JVM的并发标记清理收集器(CMS收集器)的主要目标就是:低应用停顿时间。该目标对于大多数交互式应用很重要,比如web应用。在我们看一...

低至一折起
2017/11/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【从零开始学Java笔记】Java基础

大家可以关注作者的账号,关注从零开始学Java笔记文集。也可以根据目录前往作者的博客园博客进行学习。本片文件将基于黑马程序员就业班视频进行学习以及资料的分享,并记录笔记和自己的看法。...

osc_q7wo0k6s
6分钟前
17
0
输入npm install 报错node-sass@4.13.0 postinstall:`node scripts/build.js` Failed at the node-sass@4.13.0

npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass

-乐天-
6分钟前
11
0
【从零开始学Java笔记】多态

大家可以关注作者的账号,关注从零开始学Java笔记文集。也可以根据目录前往作者的博客园博客进行学习。本片文件将基于黑马程序员就业班视频进行学习以及资料的分享,并记录笔记和自己的看法。...

osc_9na1lmr9
7分钟前
16
0
各种国内镜像源

composer国内源: 全局:composer config -g repo.packagist composer https://packagist.phpcomposer.com (来源:https://pkg.phpcomposer.com/) python国内源: 新建文件 ~/.pip/pip.con......

四季变幻
7分钟前
16
0
【从零开始学Java笔记】ArrayList类、Collection类和List类

大家可以关注作者的账号,关注从零开始学Java笔记文集。也可以根据目录前往作者的博客园博客进行学习。本片文件将基于黑马程序员就业班视频进行学习以及资料的分享,并记录笔记和自己的看法。...

osc_0g0vbf0z
7分钟前
17
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部