JVM垃圾回收器cms详解

关于serial,parnew,parallel等回收器的介绍可以参考上一篇jvm垃圾回收算法以及垃圾回收器,如何选择_bjzw的博客-CSDN博客

下面具体介绍一下cms,jdk8之后就已经废弃了cms垃圾回收器,为什么要还要详解cms呢

        首先,cms的回收思想需要我们了解

        其次,G1回收器也复用了cms的一部分思想

一、CMS回收

        CMS是在老年代的垃圾回收算法,全程为Concurrent Mark Sweep,即标记并行算法。具体有六个步骤,初始标记、并发标记、并发预处理、重新标记,并发清理、并发重置为了描述清楚,别处找了个图

       

         1、初始标记

                初始标记阶段会触发stop the world,标记所有CGRoot直接关联的对象,以及年轻代对象对老年代对象的引用,间接关联的在下一阶段。

        2、并发标记

                与用户线程并发执行,会遍历上一阶段标记过的对象进行遍历,并将这些对象所有的引用进行标记。第一,第二阶段就是要将老年代,新生代的里面存活的对象找出来

                由于是并发执行,所以会导致有遗漏的老年代对象实例,如以下情况:年轻代部分对象移动至老年代,老年代对象引用发生变更,还有一些大对象直接进入老年代。

上面三种情况的对象所在的卡表(card table)被标记为dirty。

虽然CMS收集的是老年代的对象,但是会扫描新生代。

                什么叫卡表呢?

                        就是将老年代的空间分成521B分解成若干张卡,卡表本身是个数组,数组中的每个元素对应一张卡,当有老年代引用新生代的对象时,对应的卡表的元素被标记为dirty(脏)如下图

                 并发标记就是通过卡表标记dirty,后期就不会再对老年代整个扫描了。

        3、并发预清理

                这个阶段是标记老年代存活的对象,尽量让重新标记阶段的STW时间短,这个阶段也会扫描老年代和新生代。

        4、重新标记

                这个阶段会STW,标记老年代的存活对象,这个时候扫描新生代的对象、GCRoots和之前标记为dirty的卡表老年代对象。

        5、并发清理

                清理未被标记的对象,释放内存空间

        6、并发重置

                CMS内部进行重置回收器状态,准备为下一个周期做准备。

二、如何配置CMS回收器

     1、CMS相关JVM参数
        -XX:+UseConcMarkSweepGC    激活CMS收集器。默认情况下使用ParNew + CMS + Serial Old的收集器组合进行内存回收,Serial Old作为CMS出现“Concurrent Mode Failure”失败后的后备收集器使用。

        -XX:CMSInitiatingOccupancyFraction={x}    在老年代的空间被占用{x}%时,调用CMS算法对老年代进行垃圾回收。
        -XX:CMSFullGCsBeforeCompaction={x}    在进行了{x}次CMS算法之后,对老年代进行一次compaction
        -XX:+CMSPermGenSweepingEnabled & -XX:+CMSClassUnloadingEnabled    让CMS默认遍历永久代(Perm区)
        -XX:ParallelCMSThreads={x}    设置CMS算法中并行线程的数量为{x}。(默认启动(CPU数量+3) / 4个线程。)
        -XX:+ExplicitGCInvokesConcurrent    用户程序中可能出现利用System.gc()触发系统Full GC(将会stop-the-world),利用这个参数可以指定System.gc()直接调用CMS算法做GC。
        -XX:+DisableExplicitGC    该参数直接让JVM忽略用户程序中的System.gc()

  2.注意事项

        -XX:+UseParNewGC, -XX:+UseConcMarkSweepGC
分别表示使用并行收集器 ParNew 对新生代进行垃圾回收,使用并发标记清除收集器 CMS 对老年代进行垃圾回收。

        -XX:ParallelGCThreads, -XX:ParallelCMSThreads
分别表示 Young GC 与 CMS GC 工作时的并行线程数,建议根据处理器数量进行合理设置。

        -XX:+UseCMSCompactAtFullCollection
由于 CMS GC 会产生内存碎片,且只在 Full GC 时才会进行内存碎片压缩(因此 使用 CMS 垃圾回收器避免不了 Full GC)。这个参数表示开启 Full GC 时的压缩功能,减少内存碎片。

        -XX:+UseCMSInitiatingOccupancyOnly
        -XX:CMSInitiatingOccupancyFraction
        -XX:CMSInitiatingOccupancyFraction 表示触发 CMS GC 的老年代使用阈值,一般设置为 70~80(百分比),设置太小会增加 CMS GC 发现的频率,设置太大可能会导致并发模式失败或晋升失败。

        -XX:+UseCMSInitiatingOccupancyOnly 表示 CMS GC 只基于  CMSInitiatingOccupancyFraction 触发,如果未设置该参数则 JVM 只会根据  CMSInitiatingOccupancyFraction 触发第一次 CMS GC ,后续还是会自动触发。建议同时设置这两个参数。

        -XX:+CMSClassUnloadingEnabled 表示开启 CMS 对永久代的垃圾回收(或元空间),避免由于永久代空间耗尽带来 Full GC。

        -XX:+CMSParallelRemarkEnabled 开启降低标记停顿。

A、提升失败(promotion failed)
在 Minor GC 过程中,Survivor Unused 可能不足以容纳 Eden 和另一个 Survivor 中的存活对象, 那么多余的存货对象将被移到老年代, 称为过早提升(Premature Promotion)。 这会导致老年代中短期存活对象的增长, 可能会引发严重的性能问题。

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

解决办法:

        针对一些大的朝生熄灭的对象,Surivor空间存放不下,就会存放到老年代,针对这种情况,可以扩大Survivor空间。

        老年代空间碎片,无法放下大对象,就在进行一定次数的Full GC(标记清除)的时候进行一次标记整理算法。

B、并发模式失败(concurrent mode failure)
由于cms的无法处理浮动垃圾(Floating Garbage)引起的。这个跟cms的机制有关。cms的并发清理阶段,用户线程还在运行,因此不断有新的垃圾产生,而这些垃圾不在这次清理标记的范畴里头,cms无法在本次gc清除掉,这些就是浮动垃圾。
由于这种机制,cms年老代回收的阈值不能太高,否则就容易预留的内存空间很可能不够(因为本次gc同时还有浮动垃圾产生),从而导致concurrent mode failure发生。可以通过-XX:CMSInitiatingOccupancyFraction的值来调优。

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bjzw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值