深入理解JAVA虚拟机系列之垃圾收集器CMS

一、简介

  • 本文主要介绍CMS的垃圾收器的回收流程和GC日志,也提供了在线图形化页面参考;

二、回收流程

2.1 回收流程图

2.2 回收原理

  • CMS是一种以获取最短回收停顿时间为目标的老年代回收器,采用标记-清除算法实现;
  • 回收过程分为四个阶段: 初始标记(CMS initial mark), 并发标记(CMS concurrent mark),重新标记(CMS remark),并发清除(CMS concurrent sweep);
  • 初始标记:需要STP,仅仅是标记一下GC Roots能直接关联到的对象,速度很快;
  • 并发标记:就是从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;
  • 重新标记:需要STP,为了修正并发标记期间因用户线程继续运作而导致标记产生变动的那一部分对象的标记记录,该阶段的停顿时间会比初始标记阶段稍微长一点,但也远比并发标记阶段的时间短;
  • 并发清除:清理删除掉标记阶段判断的已死亡对象,由于不需要移动存活对象,所以该阶段也是可以与用户线程同时并发的;

2.3 缺点

2.3.1 对处理器资源非常敏感

  • CMS收集器对处理器资源非常敏感,事实上,面向并发设计的程序都对处理器资源比较敏感;在并发阶段,它虽然不会导致用户线程停顿,但却因为占用了一部分线程(或者说处理器的计算能力)而导致应用程序变慢,降低总吞吐量;
  • CMS默认启动的回收线程数是(处理器核心数量 + 3)/4,也就是说,如果处理器核心数在4个或以上,并发回收时垃圾收集线程只占用不超过25%的处理器资源,并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时,CMS对用户程序的影响就可能变得很大。如果应用本来的处理器负载就很高,还要分出一半的运算能力去执行垃圾收集器线程,就可能导致用户程序的执行速度忽然大幅降低。
  • 为了缓解上述情况,虚拟机提供了一种“增量式并发收集器”(Increment Concurrent Mark Sweep/i-CMS)的CMS收集器变种,所做的事情和以前单核处理器年代PC机操作系统靠抢占式多任务来模拟多核并行多任务的思想一样,是在并发标记、并发清除的时候让收集器线程和用户线程交替运行,尽量减少垃圾收集线程的独占资源时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得较少一些,直观感受是速度变慢的时间更多了,但速度下降幅度就没有那么明显;
  • 实践证明增量式的CMS收集器效果很一般,从JDK7开始,i-CMS模式已经被声明为“deprecates”,即已过时不再提倡用户使用,到JDK9发布收i-CMS模式被完全废弃;

2.3.2 浮动垃圾

  • 浮动垃圾:在CMS的并发标记和并发清除的过程中,用户线程还是继续执行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中清理掉它们,只好留待下一次垃圾收集时再清理掉,这一部分垃圾就称为浮动垃圾;
  • 同样也是由于在垃圾收集阶段用户线程还需要持续运行,那就还需要预留足够内存空间提供给用户线程使用,因此CMS收集器不能像其他收集器那样等待到老年代几乎完全被填满了再进行收集,必须预留一部分空间供并发收集时的程序运作使用。在JDK5的默认设置下,CMS收集器当老年代使用了68%的空间后就会被激活,这是一个偏保守的设置,如果在实际应用中老年代增长并不是太快,可以适当调高参数-XX:CMSInitiatingOccu-pancyFraction的值来提高CMS的触发百分比,降低内存回收频率,获取更好的性能。到了JDK6时,CMS收集器的启动阈值就已经默认提升至92%;
  • 由于CMS收集器无法处理浮动垃圾,有可能出现“Concurrent Mode Failure”失败而导致另一次完全的STP的Full GC的产生。要是CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败”,这时候虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集,但这样停顿时间就很长了。所以参数-XX:CMSInitiatingOccu-pancyFraction设置的太高将会很容易导致大量的并发失败产生,性能反而降低,用户应在生产环境根据实际应用情况来权衡设置;

2.3.3 空间碎片问题

  • CMS是一款基于标记清除算法实现的收集器,收集结束时自然会有大量空间碎片产生。空间碎片过多,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次Full GC的情况;
  • 为了解决上述问题,CMS提供了要给-XX:UseCMS-CompaceAtFullCollection开关参数(默认时开启的,此参数从JDK9开始废弃),用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程,由于这个内存整理必须移动存活对象,(在Shenandoah和ZGC出现前)时无法并发的,这样得以解决空间碎片问题;
  • 上面方案虽说解决了空间碎片问题,但是停顿时间又变长了,因此虚拟机设计者还提供了另一个参数-XX:CMSFullGCsBefore-Compaction(此参数从JDK9开始废弃),这个参数的作用是要求CMS收集器在执行若干次(数量由参数值确定)不整理空间的Full GC之后,下一次进入Full GC前会先进行碎片整理(默认值为0,表示每次进入Full GC时都进行碎片整理);

三、GC日志分析

四、图形化参考

posted @ 2021-01-23 23:28  请叫我猿叔叔  阅读(591)  评论(0)    收藏  举报