CMS 垃圾收集流程

CMS 垃圾收集流程,第1张

CMS 处理过程有七个步骤:

在Java语言里,可作为GC Roots对象的包括如下几种:

从“初始标记”阶段标记的对象开始找出所有存活的对象

因为是并发运行的,在运行期间会发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代;

并发标记阶段只负责将引用发生改变的Card标记为Dirty状态,不负责处理;

如下图所示,也就是节点1、2、3,最终找到了节点4和5。并发标记的特点是和应用程序线程同时运行。并不是老年代的所有存活对象都会被标记,因为标记的同时应用程序会改变一些对象的引用等。

由于这个阶段是和用户线程并发的,可能会导致 concurrent mode failure

1) promotion failed不会导致fullGC中的CMS退化为serialGC

因为promotion fail后,youngGC就被停止了,开始fullGC。

2)发生concurrent mode failure会引起Full GC,这种情况下会使用Serial Old收集器,是单线程的,对GC的影响很大。concurrent mode failure产生的原因是老年代剩余的空间不够,导致了和gc线程并发执行的用户线程创建的大对象(由PretenureSizeThreshold控制新生代直接晋升老年代的对象size阀值)不能进入到老年代,只要stop the world来暂停用户线程,执行GC清理,单线程对全堆以及 metaspace 进行回收,STW 的时间会特别长,对业务系统的可用性影响比较大。可以通过设置CMSInitiatingOccupancyFraction预留合适的CMS执行时剩余的空间

一般CMS的GC耗时80%都在remark阶段,如果发现remark阶段停顿时间很长,可以尝试添加该参数:

-XX:+CMSScavengeBeforeRemark。

在执行remark操作之前先做一次Young GC,目的在于减少年轻代对老年代的无效引用,降低remark时的开销

参考:

CMS的缺点:

1.浮动垃圾:由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然会有新垃圾产生,这部分垃圾得标记过程之后,所以CMS无法在当收集中处理掉他们,只好留待下一次GC清理掉,这一部分垃圾称为浮动垃圾。在jdk1.5默认设置下,CMS收集器当老年代使用了68%的空间就会被激活,可以通过-XX:CMSInitialOccupancyFraction的值来提高触发百分比,在jdk1.6中CMS启动阈值提升到了92%,要是CMS运行期间预留的内存无法满足程序的需要,就会出现”Concurrent Mode Failure“,然后降级临时启用Serial Old收集器进行老年代的垃圾收集,这样停顿时间就很长了,所以-XX:CMSInitialOccupancyFraction设置太高容易导致大量”Concurrent Mode Failure“。

2.有空间碎片:CMS是一款基于“标记-清除”算法实现的,所以会产生空间碎片。为了解决这个问题,CMS提供了-XX:UseCMSCompactAtFullCollection开发参数用于开启内存碎片的合并整理,由于内存整理是无法并行的,所以停顿时间会变长。还有-XX:CMSFullGCBeforeCompaction,这个参数用于设置多少次不压缩Full GC后,跟着来一次带压缩的(默认为0)。

3.对CPU资源敏感,CMS默认启动的回收线程数是(cpu数量+3)/4。所以CPU数量少会导致用户程序执行速度降低较多。


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/417638.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-24
下一篇2023-05-24

发表评论

登录后才能评论

评论列表(0条)

    保存