24、JVM实战:垃圾收集器:CMS

19.6–垃圾收集器–CMS


1、结构图

*

2、CMS(Concurrent Mark Sweep)收集器

*

2.1、特征

1、 一种以获取最短回收停顿时间为目标的收集器;
2、 适用于:重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验的应用;
3、 CMS收集器是基于"标记-清除"算法实现的;

2.2、运作过程分为4个步骤

2.2.1、初始标记(CMS initial mark)

需要"Stop The World",仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。

2.2.2、并发标记(CMS concurrent mark)

1、 就是进行GCRootsTracing(跟踪)的过程,就是判断哪些对象不可达的过程;
2、 与用户线程一起工作;

2.2.3、重新标记(CMS remark)

需要"Stop The World",是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

2.2.4、并发清除(CMS concurrent sweep)

1、 与用户线程一起工作;

总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行。

2.3、优点

并发收集、低停顿。

3.4、缺点

3.4.1、CMS收集器对CPU资源非常敏感。

对于并发实现的收集器而言,虽然可以利用多核优势提高垃圾收集的效率,但是由于收集器在运行过程中会占用一部分的线程,这些线程会占用CPU资源,所以会影响到应用系统的运行,会导致系统总的吞吐量降低。

CMS默认开始的回收线程数(t)
t=(cpu数量 + 3) / 4

垃圾回收线程占用的CPU资源*
c=(cpu数量 + 3) / (4*cpu数量)=1/4+3/(4*cpu数量)

假设1个CPU,c=100%
假设2个CPU,c=62.5%
假设3个CPU,c=50%
假设4个CPU,c=43.75%
假设5个CPU,c=40%
假设6个CPU,c=37.5%
假设7个CPU,c=35.71%
假设8个CPU,c=34.37%

结论

1、 垃圾回收线程占用的CPU资源至少大于%25;
2、 随着CPU数量的增加,垃圾回收线程占用的CPU资源会减少;
3、 当CPU资源少于4个的时候,垃圾回收线程占用的CPU资源的比例会增大;
4、 在选用CMS收集器的时候,需要考虑,当前的应用系统,是否对CPU资源敏感;

3.4.2、垃圾收集的过程中,无法处理浮动垃圾,所以可能会出现Concurrent Mode Failure问题而导致触发一次Full GC。

浮动垃圾:是由于CMS收集器的并发清理阶段,清理线程是和用户线程一起运行,如果在清理过程中,用户线程产生了垃圾对象,由于过了标记阶段,所以这些垃圾对象就成为了浮动垃圾,CMS无法在当前垃圾收集过程中集中处理这些垃圾对象。

由于这个原因,CMS收集器不能像其他收集器那样等到完全填满了老年代以后才进行垃圾收集,需要预留一部分空间来保证当出现浮动垃圾的时候可以有空间存放这些垃圾对象。

在JDK 1.6中,CMS收集器的激活阀值变成了92%。如果在CMS运行期间没有足够的内存来存放浮动垃圾,那么就会导致Concurrent Mode Failure失败,这个时候,虚拟机将启动后备预案,临时启动Serial Old收集器来对老年代重新进行垃圾收集,这样会导致垃圾收集的时间边长,特别是当老年代内存很大的时候。所以对参数-XX:CMSInitiatingOccupancyFraction的设置,过高,会导致发生Concurrent Mode Failure,过低,则浪费内存空间。

3.4.3、使用的"标记-清除"算法会出现很多内存碎片。

过多的内存碎片会影响大对象的分配,会导致即使老年代内存还有很多空闲,但是由于过多的内存碎片,不得不提前触发垃圾Full GC。

为了解决这个问题,CMS收集器提供了一个"-XX:+UseCMSCompactAtFullCollection"参数(默认是开启的),用于CMS收集器在必要的时候对内存碎片进行压缩整理。由于内存碎片整理过程不是并发的,所以会导致停顿时间变长。

虚拟机还提供了一个-XX:CMSFullGCsBeforeCompaction"参数,来控制进行过多少次不压缩的Full GC以后,进行一次带压缩的Full GC,默认值是0,表示每次在进行Full GC前都进行碎片整理。

3.5、参数

1、 -XX:+UseConcMarkSweepGC:使用CMS收集器;
2、 -XX:+UseCMSCompactAtFullCollection:FullGC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长;
3、 -XX:+CMSFullGCsBeforeCompaction设置进行几次FullGC后,进行一次碎片整理;
4、 -XX:ParallelCMSThreads设定CMS的线程数量(一般情况约等于可用CPU数量);