一、简介

Concurrent Mark Sweep,是一种以获取最短回收停顿时间为目标的收集器,尤其重视服务的响应速度。

CMS是老年代垃圾回收器,基于标记-清除算法实现。新生代默认使用ParNew收集器,基于复制算法

二、垃圾回收过程

分为四个步骤进行垃圾回收:初始标记,并发标记,重新标记,并发清除。只有初始标记和重新标记需要停顿。

  • 初始标记。只是标记一下GC Roots能直接关联到的老年代对象,速度很快。这一阶段会STW
  • 并发标记。就是进行GC Roots的Tracing,处理器可以与用户线程一起工作。
  • 重新标记。为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段也会触发STW,停顿时间会比初始标记阶段稍长,远比并发时间短。
  • 并发清除,处理器可以与用户线程一起工作。

可达性分析方法

  • 通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为“引用链”,当一个对象到GC Roots没有任何引用链相连时,说明这个对象是可回收的。
  • Java语言中,可作为GC Roots的对象包括以下几种:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中引用的对象。

三、垃圾回收触发时机

新生代垃圾回收触发时机:当 eden 区内存无法为一个新对象分配内存时,就会触发 Minor GC

老年代垃圾回收触发时机:

1、如果没有设置-XX:+UseCMSInitiatingOccupancyOnly,虚拟机会根据收集的数据决定是否触发(建议线上环境带上这个参数,不然会加大问题排查的难度)。
2、老年代使用率达到阈值 CMSInitiatingOccupancyFraction,默认92%。
3、永久代的使用率达到阈值 CMSInitiatingPermOccupancyFraction,默认92%,前提是开启 CMSClassUnloadingEnabled
4、新生代的晋升担保失败。老年代没有足够的空间来容纳全部的新生代对象或历史平均晋升到老年代的对象,如果不够的话,就提早进行一次老年代的回收,防止下次进行YGC的时候发生晋升失败。

四、垃圾回收如何做到STW

JVM有个叫做“安全点”和“安全区域”的东西,在发生GC时,所有的线程都会执行到“安全点”停下来。
在需要GC的时候,JVM会设置一个标志,当线程执行到安全点的时候会轮询检测这个标志,如果发现需要GC,则线程会自己挂起,直到GC结束才恢复运行。

但是对于一些没有获得或无法获得CPU时间的线程,就没办法等到它执行到安全点了,所以这个时候只要这个线程是在安全区域的,也可以进行GC,安全区域是一段代码段,在这段代码段中对象的引用关系不会发生变化,所以这个时候进行GC也是安全的。

五、关于FullGC

  • Full GC == Major GC指的是对老年代/永久代的stop the world的GC
  • Full GC的次数 = 老年代GC时 stop the world的次数
  • Full GC的时间 = 老年代GC时 stop the world的总时间
  • CMS 不等于Full GC,我们可以看到CMS分为多个阶段,只有stop the world的阶段被计算到了Full GC的次数和时间,而和业务线程并发的GC的次数和时间则不被认为是Full GC。CMS主要可以分为initial mark(stop the world), concurrent mark, remark(stop the world), concurrent sweep几个阶段,其中initial mark和remark会stop the world。

六、CMS缺点

  • 垃圾回收时会占用一部分线程,导致系统变慢,总吞吐量会降低。
  • 无法处理浮动垃圾,需要预留足够的内存空间给用户线程使用,可以通过 -XX:CMSInitiatingOccupancyFraction 参数控制触发垃圾回收的阈值。
  • 如果预留的内存无法满足程序需要,就会出现“Concurrent Mode Failure”失败,这时将启动应急预案,启用Serial Old 进行垃圾回收,停顿时间会变长。所以-XX:CMSInitiatingOccupancyFraction 参数的值设置的太高,会导致频繁“Concurrent Mode Failure”失败,性能反而降低。
  • 标记-清理,容易产生内存碎片。-XX:+UseCMSCompactAtFullColletion 开启碎片整理功能,默认开启,-XX:CMSFullGCsBeforeCompaction,控制多少次不压缩的FullGC之后来一次带压缩的

七、常见问题

1、promotion failed – concurrent mode failure

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

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

2、concurrent mode failure

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

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

3、碎片整理

-XX:+UseCMSCompactAtFullCollection  强制进行空间碎片整理
CMS 采用标记算法,会产生大量的空间碎片。以上参数就是强制执行一次空间碎片整理,但是空间碎片整理会引发STW。

-XX:+CMSFullGCsBeforeCompaction 配置经过几次的FullGC进行空间碎片整理
-XX:+CMSFullGCsBeforeCompaction=10  经过10次FGC后进行空间碎片整理,以降低STW次数

【JVM】CMS垃圾回收器的更多相关文章

  1. jvm——CMS 垃圾回收器(未完)

    https://matt33.com/2018/07/28/jvm-cms/ 阶段1:Initial Mark stop-the-wolrd 标记那些直接被 GC root 引用或者被年轻代存活对象所 ...

  2. JVM七大垃圾回收器上篇Serial、ParNeW、Parallel Scavenge、 Serial Old、 Parallel Old、 CMS、 G1

    GC逻辑分类 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本. 从不同角度分析垃圾收 ...

  3. 【JVM】垃圾回收器总结(2)——七种垃圾回收器类型

    七种垃圾回收器类型 GC的约定参数 DefNew——Default New Generation Tenured——Serial Old ParNew——Parallel New Generation ...

  4. JVM七大垃圾回收器下篇G1(Garbage First)

    G1回收器:区域化分代式 既然我们已经有了前面几个强大的GC,为什么还要发布Garbage First (G1)GC?  原因就在于应用程序所应对的业务越来越庞大.复杂,用户越来越多,没有GC就不能保 ...

  5. 探索ParNew和CMS垃圾回收器

    前言 上篇文章我们一起分析了JVM的垃圾回收机制,了解了新生代的内存模型,老年代的空间分配担保原则,并简单的介绍了几种垃圾回收器.详细内容小伙伴们可以去看一下我的上篇文章:秒懂JVM的垃圾回收机制. ...

  6. 关于 CMS 垃圾回收器,你真的懂了吗?

    大家好,我是树哥. 前段时间有个小伙伴去面试,被问到了 CMS 垃圾回收器的详细内容,没答出来.实际上,CMS 垃圾回收器是回收器历史上很重要的一个节点,其开启了 GC 回收器关注 GC 停顿时间的历 ...

  7. 深入理解JVM一垃圾回收器

    上一篇我们介绍了常见的垃圾回收算法,不同的算法各有各的优缺点,在JVM中并不是单纯的使用某一种算法进行垃圾回收,而是将不同的垃圾回收算法包装在不同的垃圾回收器当中,用户可以根据自身的需求,使用不同的垃 ...

  8. jvm学习-垃圾回收器(四)

    说明 各种垃圾回收算法都有各自的优缺点.jvm也并没有只采用一种垃圾算法.并提供几种组合供我根据场景进行选择. jvm内存结构 Person p=new Person(); 1.程序里面创建一个对象会 ...

  9. 深入探究JVM之垃圾回收器

    @ 目录 前言 正文 一.垃圾收集算法 标记-复制 标记-清除 标记-整理 分代回收 二.常用的垃圾回收器 Serial/SerialOld ParNew Parallel Scavenge/Para ...

随机推荐

  1. flask实战-个人博客-电子邮件支持

    电子邮件支持 因为博客要支持评论,所以我们需要在文章有了新评论后发邮件通知管理员.而且,当管理员回复了读者的评论后,也需要发送邮件提醒读者. 为了方便读者使用示例程序,personalBlog中仍然使 ...

  2. linux下使用shell发送http请求

    一.curl 1. get请求 curl命令默认下就是使用get方式发送http请求. curl www.baidu.com 2. post请求 使用-d参数,形式如下: curl -d " ...

  3. ESP8266 SDK开发: 测试下诱人的程序

    前言 这一节测试一下诱人的程序 实现的功能,APP通过SmartConfig给Wi-Fi模块配网并绑定设备,然后通过MQTT远程控制开发板的继电器, APP显示ESP8266采集的温湿度数据. 简而言 ...

  4. 在eclipse中新建java问题报错:The type XXX cannot be resolved. It is indirectly referenced from required .class files

    在Eclipse中遇到The type XXX cannot be resolved. It is indirectly referenced from required .class files错误 ...

  5. bat修改文件内容

    #file.vbsSet fso = Wscript.CreateObject("Scripting.FileSystemObject")set f=fso.opentextfil ...

  6. Redis哨兵、复制、集群的设计原理与区别

    一 前言 谈到Redis服务器的高可用,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. 哨兵(Sentinel):可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转 ...

  7. Linux 系统开机时间及当前时间

    最近一次系统开机时间:date -d "$(awk -F. '{print $1}' /proc/uptime) second ago" +"%Y-%m-%d %H:%M ...

  8. ASP.NET Core消息队列RabbitMQ基础入门实战演练

    一.课程介绍 人生苦短,我用.NET Core!消息队列RabbitMQ大家相比都不陌生,本次分享课程阿笨将给大家分享一下在一般项目中99%都会用到的消息队列MQ的一个实战业务运用场景.本次分享课程不 ...

  9. 【Python开发】Pycharm下的Anaconda配置

    我的系统是Win 64位的,用的Python 3.5.1 ,最近在学机器学习,用到了Numpy这个科学计算库,网上查了之后,看到很多装Numpy出问题的情况,所以决定装Anaconda,简单一些,并且 ...

  10. Qt 实现超时锁屏

    最近使用Qt实现超时锁屏的功能(工控机触摸屏),当手长时间不触摸屏幕的时候,程序超时会显示锁屏窗口. 一.效果 主窗口超时显示锁屏窗口: 系统窗口超时显示锁屏窗口: 二.实现思路 首先开启一个线程用于 ...