JVM学习笔记(5):CMS垃圾回收器工作原理详解

本文深入解析CMS垃圾回收器的工作原理,包括其四个阶段:初始标记、并发标记、重新标记和并发清理,以及性能分析,讨论了并发回收对CPU资源的影响、ConcurrentModeFailure问题和内存碎片处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

老年代回收容器——CMS

1、CMS工作原理

  一般老年代我们选择的垃圾回收器是CMS,他采用的是标记清理算法(不是标记整理)。之前提到过Stop the World状态,就是垃圾回收时停止一切线程的工作,如果在这个状态下再去慢慢执行标记清理算法,会导致系统卡死时间过长,所以CMS垃圾回收器采取的是垃圾回收线程和系统工作线程尽量同时执行的模式来处理的。

  工作原理:为了避免长时间Stop the World,CMS采用了4个阶段来垃圾回收,分别是初始标记、并发标记、重新标记和并发清理。其中初始标记和重新标记,耗时很短,虽然会导致Stop the World,但是影响不大,然后并发标记和并发清理,两个阶段耗时最长,但是是可以跟系统的工作线程并发运行的,所以对系统没太大影响。

2、CMS垃圾回收的四个阶段

(1)初始标记——标记直接GC Roots(直接)

  在这个阶段让系统的工作线程全部停止,进入Stop the World状态。同时标记所有GC Roots直接引用的对象,是直接引用!比如下面这段代码,仅仅会通过replicaManager这个类的静态变量代表的GC Roots,去标记出来他直接引用的ReplicaManager对象,不会去管ReplicaFetcher这种对象,因为ReplicaFetcher对象是被ReplicaManager类的replicaFetcher实例变量引用的。(之前说过,方法的局部变量和类的静态变量是GC Roots。但是类的实例变量不是GC Roots。)

public class Kafka {
    private static ReplicaManager replicaManager = new ReplicaManager();
}
public class ReplicaManager {
    private ReplicaFetcher replicaFetcher = new ReplicaFetcher();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fk34GSB4-1581153515938)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210028357.png)]

初识标记如图所示

(2)并发标记——对老年代所有对象进行GC Roots追踪(最耗时)

  这个阶段会让系统线程可以随意创建各种新对象,继续运行。在运行期间可能会创建新的存活对象,也可能会让部分存活对象失去引用,变成垃圾对象。在这个过程中,垃圾回收线程,会尽可能的对已有的对象进行GC Roots追踪。GC Roots追踪,意思就是对类似ReplicaFetcher之类的全部老年代里的对象,他会去看他被谁引用了,认定为是被GC Roots间接引用后,就不需要回收它。因为老年代里存活对象是比较多的,这个过程会追踪大量的对象,所以耗时较高。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4a1EWItV-1581153515940)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210352901.png)]

(3)重新标记

  第二阶段里,你一边标记存活对象和垃圾对象,一边系统在不停运行创建新对象,让老对象变成垃圾,所以第二阶段结束之后,绝对会有很多存活对象和垃圾对象,是之前第二阶段没标记出来的。在这个阶段,要再次进入Stop the World阶段,重新标记下在第二阶段里新创建的一些对象,还有一些已有对象可能失去引用变成垃圾的情况。重新标记的阶段只是对变动过的少数对象进行标记,是速度很快的

(4)并发清理

  这个阶段就是让系统程序随意运行,然后清理掉之前标记为垃圾的对象即可,也是很耗时的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfWztAVu-1581153515941)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023210811272.png)]

3、CMS性能分析

(1)好的方面

  CMS的第二阶段和第四阶段,都是很耗时的,但都和系统程序是并发执行的,所以基本这两个最耗时的阶段对性能影响不大。只有第一个阶段和第三个阶段是需要Stop the World的,但是这两个阶段都是简单的标记而已,速度非常的快,所以基本上对系统运行响应也不大。

(2)坏的方面
①并发回收导致CPU资源紧张。

  并发标记和并发清理两个最耗时的阶段,使垃圾回收线程和系统工作线程同时工作,导致有限的CPU资源被垃圾回收线程占用了一部分。在这两个阶段,CMS的垃圾回收线程是比较耗费CPU资源的。CMS默认启动的垃圾回收线程的数量是(CPU核数 + 3)/ 4,比如的2核4G机器,就会占用(2+3)/4 = 1个CPU被用来垃圾回收。

②Concurrent Mode Failure问题

  在并发清理阶段,CMS只不过是回收之前标记好的垃圾对象,但这个时候系统一直在运行,先把某些对象分配在新生代,然后可能触发了一次Minor GC,一些对象进入了老年代,在短时间内又没人使用这些对象,这种垃圾对象就是浮动垃圾,虽然它是垃圾,但是不会回收他们,要等到下一次才能回收。

  CMS垃圾触发的时机是当老年代内存占用到达一定比例时,就会自动GC-XX:CMSInitiatingOccupancyFaction这个参数可以设置老年代内存占用到多少比例时触发垃圾回收。JDK 1.6默认是92%。预留8%的空间给并发回收期间,系统程序把一些新对象放入老年代中。如果垃圾回收期间,要放入的对象大于可用内存空间,就会发生Concurrent Mode Failure,即并发垃圾回收失败了,我一边回收,你一边把对象放入老年代,内存都不够了。此时就会自动用Serial Old垃圾回收器替代CMS,就是直接强行把系统程序Stop the World,重新进行长时间的GC Roots追踪,标记出来全部垃圾对象,不允许新的对象产生。

③内存碎片问题

  老年代的CMS采用标记清理算法(不是标记整理),每次都是标记出来垃圾对象,然后一次性回收掉,这样会导致大量的内存碎片产生,太多的内存碎片实际上会导致更加频繁的Full GC。

  CMS有一个参数是-XX:+UseCMSCompactAtFullCollection,默认是打开的,意思是在Full GC之后要再次进行Stop the World,停止工作线程,然后进行碎片整理,就是把存活对象挪到一起,空出来大片连续内存空间,避免内存碎片。

  还有一个参数是-XX:CMSFullGCsBeforeCompaction,这个意思是执行多少次Full GC之后再执行一次内存碎片整理的工作,默认是0,意思就是每次Full GC之后都会进行一次内存整理,存活对象都放在一起,然后空出来大片连续内存空间可供使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFVPZWhV-1581153515942)(/Users/zhangye/Library/Application Support/typora-user-images/image-20191023214004334.png)]
红圈处即浮动垃圾

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值