我的JVM(七):十种垃圾回收器和三色标记法

一、概述

    在上文《我的JVM(六):GC的基础概念以及GC算法》中我们了解了到jdk14.0的十种垃圾回收器,本文来详细介绍一下各种垃圾回收器的底层原理以及采用的算法。

二、分析

    1. 十种垃圾回收器

    2. Serial垃圾回收器

    Serial和Serial Old这两种垃圾处理器已经很少见了,它们两的原理相同,只是用于的区域不同,Serial用于新生代,Serial Old用于老年代。我们来看一下它们的工作原理,如下图:

    Serial垃圾回收器在每次进行GC操作的时候,会停止所有的业务(这个操作简称STW),然后由一个GC单线程来做垃圾回收。如果缩短STW的时间,是JVM调优的一个很严重的话题。

    3. Parallel Scavenge垃圾回收器

    当Serial垃圾回收器的GC单线程处理不过来时,就需要GC多个线程来处理了,这就是我们的Parallel Scavenge。Parallel Scavenge与Paralle Old工作原理也相同,如下图:

    4. ParNew垃圾回收器

    ParNew是来自Parallel Scavenge的增强版本,使其可以配合CMS垃圾回收器进行使用。Parallel Scavenge无法直接配合CMS进行使用,所以诞生了ParNew。

  

    5. CMS垃圾回收器

    随着行业发展,内存由最开始的几兆到现在几十G,以前的GC单线程回收STW时间也不会太长,而现在就不适用,那么在这个时候就诞生了CMS。虽然CMS臭名昭著,但是却有着承上启下的作用,有着非常重要的历史地位和意义。从CMS才开始有并发的垃圾回收模型。

    从线程角度思考模式如下:

    一共分为4个大阶段:

        (1) 初始标记:这个阶段是STW的,但是只标记根对象,所以耗时不多。

        (2) 并发标记:这个阶段是最难最耗时的。业务线程依然在继续拓展对象树;而GC线程也在进行标记,标记出垃圾。这个阶段存在两个严重的问题:

            第一:当GC线程将一块内存标记为垃圾了,但是业务线程里又有对象指向了此内存,那么就产生了错标。为了解决这个问题,我们利用重新标记阶段进行二次标记。

            第二:当GC线程标记一块内存不是垃圾后,业务线程又删除了对这块内存的引用,那么就产生了漏标。漏标没有做处理,因为下一次回收也能够标记并清除,但是这就导致CMS产生了浮动垃圾(指漏标),浮动垃圾多了就会造成内存的碎片化。

        (3) 重新标记:这个阶段是为了解决错标的问题,并且也是STW的。STW后,再次扫描所以对象,看看有没有错标的,因为线程栈中有缓存,所以这块耗时也是可以接受的(但是对象特别大特别复杂时依然很慢,这就是CMS隐藏的巨大BUG),那么标记过程采用的是什么方式来缩短耗时的呢?java语音和go语音都采用的是著名的三色标记算法。ZGC采用的算法是颜色指针(Colored Pointor)。G1和CMS都采用的是三色标记算法。

        (4) 并发清理:在完成重新标记后,GC线程与业务线程并行,GC线程对标记好的垃圾对象进行清除。

    6. 并发标记算法-三色标记法

    三色标记法采用白、灰、黑三种颜色标记对象,如下图:

    我们由三色标记算法并发标记过程来分析什么时候产生了漏标:

        (1) 最开始A指向B,B指向D,但是在A完成标记后,标记到了B还没标记D时,B指向D的引用消失, 而A有指向了D,此时因为A标记为黑色表示所有子对象都被标记,所以D不会被检测到,从而导致漏标。如下图:

        为了解决上诉问题,CMS给出了自己的解决方案,就是一个对象A的引用指向了另一个对象D,那么将这个对象A重新标记为灰色(这种方式也有bug,见下文),如下:

        (2) 由CMS的Incremental Update而产生的漏标问题。如下图:

        要出现上诉情况分为以下几步:

            (1) GC线程m1正在标记对象A,并且对象A中的属性1已经完成标记,还未完成属性2的标记,所以此时对象A为灰色。

            (2) 此时业务逻辑线程m2将对象A的属性1指向对象D,根据Incremental Update规则,此时A应该被GC线程m2更改为灰色,但是因为对象A的属性2并未完成标记,所以A本来就是灰色,并没有变化。

            (3) GC线程m1完成了对对象A的属性2的标记,因为对象A的属性1在线程m1看来已经完成了标记,而不会再去看属性1是否有引用到对象D,所以将对象A标记为黑色。所以对象D被漏标了。

    7. G1垃圾回收器

    G1垃圾回收器所采用的策略是不在物理上进行分区,将内存分为一小块一小块,每一小块可以是任何一种区域,也就是从逻辑上进行分代。如下图:

    这样做有什么好处呢?G1的做法概括起来就是分而治之。哪一块内存要满了,GC只需要回收这一块内容,这样其他线程在其他块产生对象,而要回收的区域同步进行回收,效率上得到巨大的提升,并且不会产生卡顿。

    由于在CMS中我们了解到三色标记法会产生漏标,并且CMS有自己的处理方案,那么G1也给出了自己的方案,如下图:

    G1的思路是,每次引用变化,都将这个引用存到队列中,下一次扫描的时候,查看这些引用拿出来,看一下这些引用指向的对象是否有用,这种方式需要配合G1自身的Remeber Set使用。

三、总结

    本文主要介绍了几种常用的垃圾回收器的底层原理和并发标记的三色标记算法,重点是三色标记算法的原理以及CMS是如何处理自己标记算法存在的缺陷的。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Java觉浅

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

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

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

打赏作者

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

抵扣说明:

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

余额充值