点击上方 好好学java ,选择 星标 公众号

重磅资讯、干货,第一时间送达
今日推荐:2020,搞个 Mac 玩玩!个人原创+1博客:点击前往,查看更多
作者:藤伦柳揶
链接:https://www.jianshu.com/p/dbd32622ad20

1.问题预览

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

  • promotion failed

  • 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]

2.原因和解决方案

首先我们经常遇到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的触发,以保证旧生代有足够的空间。

3.总结

promotion failed – concurrent mode failure

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

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

concurrent mode failure

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

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

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

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

  1. 0 full gc时cpu idle_结合GC日志讲讲CMS垃圾收集器

    1 CMS垃圾收集器介绍 CMS(Concurrent Mark Sweep)收集器旨在获取最短回收停顿时间的并发垃圾收集器.CMS基于"标记-清除"算法实现,并发指的是CMS的垃 ...

  2. CMS垃圾收集器详解

    概述 CMS垃圾收集器是一款优秀的老年代并发垃圾收集器,通过与用户线程并发执行的方式减少GC停顿的时间.本文主要聊一下CMS设计到的相关的数据结构.具体的执行过程.运行中会出现的异常情况. 在CMS之 ...

  3. CMS垃圾收集器和G1垃圾收集器

    CMS CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的老年代收集器.CMS收集器与之前的垃圾收集器最大的特点就是它可以并发清除垃圾. 他的工作流程如下: ...

  4. CMS垃圾收集器详解(转载)

    文章目录 概念 CMS的GC过程 初始标记 并发标记 并发预处理 重新标记 并发清除 CMS的缺点 总结: 概念 CMS全称为Concurrent Mark Sweep,即 并发标记清除,对比其他的收 ...

  5. 面试官:今天还是来聊聊CMS垃圾收集器呗?

    面试官:今天还是来聊聊CMS垃圾收集器呗? 候选者:嗯啊- 候选者:如果用Seria和Parallel系列的垃圾收集器:在垃圾回收的时,用户线程都会完全停止,直至垃圾回收结束! 候选者:CMS的全称: ...

  6. 删除已弃用的CMS垃圾收集器的JEP草案

    在2017年4月10日的文章" Java垃圾收集器:G1GC何时将CMS强制退出? ",我讨论了JEP 291 ("弃用并发标记扫描(CMS)垃圾收集器"),并 ...

  7. 垃圾收集器总结--CMS垃圾收集器

    目录 一.CMS 1.1 概述 1.2 内存碎片 1.3 浮动垃圾 1.4 空间预留 1.4 Promotion Failed和Concurrent Mode Failure 1.5 常用参数 二.总 ...

  8. 【深入理解JVM】CMS垃圾收集器

    GC 问题处理能力能不能系统性掌握?一些影响因素都是互为因果的问题该怎么分析?比如一个服务 RT 突然上涨,有 GC 耗时增大.线程 Block 增多.慢查询增多.CPU 负载高四个表象,到底哪个是诱 ...

  9. 【面试高频题】CMS垃圾收集器是如何工作的?

    CMS全称 Concurrent Mark Sweep,是一款并发的.使用标记-清除算法的垃圾回收器,以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,对于要求服务器响应速度的应用上,这种垃圾回收 ...

最新文章

  1. HDU 2498 Digits
  2. [C#]使用HttpWebRequest请求远端服务器时如何加载SSL证书
  3. 攻防世界dice_game(pwn)
  4. jvm 06-G1收集器
  5. Shell_Shell调用SQLPlus简介(案例)
  6. Java 笔记(二)
  7. ETL学习之八:添加日志记录
  8. 190107每日一句
  9. windows 安装 perl 教程
  10. 《我的初恋、我的老婆》超爆笑!!
  11. OpenGL3D图形、旋转、纹理、键盘移动、光照、滤波、透明(完整)
  12. U盘中毒后,手把手教你恢复文件
  13. java 表格 列删除_Java 删除Excel表格中的空白行或列
  14. 抽丝剥茧Reactor模式
  15. JavaScript ES5-ES6详解
  16. 租车出行如何避免踩坑,区块链技术解决信任问题
  17. 15.全文检索-ElasticSearch
  18. 关于ABB软件的使用和干涉区设定
  19. IJPay 聚合支付系列文章
  20. python操作数据库教程_Python连接mysql数据库及简单增删改查操作示例代码

热门文章

  1. Linux platform总线(1):总体框架
  2. 各个级别镜像之间的跳转模型
  3. [ARM异常]-ARMV8的中断的routing和Mask表
  4. android推送如何推送不在线设备,推送系统从0到1(四):消息如何到达用户设备...
  5. Ubuntu下pip安装、升级、卸载
  6. 【安全技术】关于几种dll注入方式的学习
  7. python封装一个效率极高的 批量更新、插入合一的工具
  8. 利用SSH 反向代理 ,实现跨局域网连接家里的linux 主机 (树莓派)
  9. ListView 设置高度为刚好能包裹子元素
  10. MySQL删除用户权限(REVOKE)