JVM 垃圾回收器 ZGC垃圾回收算法的实现

JVM 垃圾回收器 ZGC垃圾回收算法的实现

学习ZGC,主要通过学习ZGC设计与实现书籍,并以博客的形式记录学习内容
ZGC是一款并发的垃圾回收器,支持并发标记、并发转移和并发重定位。

并发垃圾回收算法

并发垃圾回收算法概述

并发垃圾回收算法实际上是以复制算法为基础,增加了并发处理。
复制算法分为3个算法:

  • 标记:从根集合出发,标记活跃对象;此时内存中存在活跃对象和已死亡对象。
  • 转移:把活跃对象转移(复制)到新的内存上,原来的内存空间可以回收。
  • 重定位:因为对象的内存地址发生了变化,所以所有指向对象老地址的指针都要调整到对象新的地址上。

在这里插入图片描述

ZGC并发算法的设计

ZDriver是执行垃圾回收的控制线程,可以说ZDriver就是ZGC并发垃圾回收算法的实现。
在这里插入图片描述
整个垃圾回收算法包含10个步骤,来看一下具体每一步所完成的工作:

  1. 初始标记:该步骤从根集合出发,找出根集合直接引用的活跃对象,并入栈;该步需要STW。
  2. 并发标记:根据初始标记找到的对象,作为并发标记的根对象,使用深度优先遍历对象的成员变量进行标记;并发标记需要解决标记过程中引用关系变化导致的漏标记问题,具体方法后面介绍。
  3. 再标记和非强根并行标记:在并发标记结束后尝试终结标记动作,理论上并发标记结束后所有待标记的对象会全部完成,但是因为GC工作线程和应用线程是并发运行,所以可能存在GC工作线程执行结束标记时,应用程序线程又有新的引用关系变化导致漏标记,所以这一步先判断是否真的结束了对象的标记,如果没有结束就还会启动并行标记,所以这一步需要STW。另外,在该步中,还会对非强根进行并行标记。
  4. 并发处理非强引用和非强根并发标记:在非强引用处理时对定义了finalize()函数的对象需要特殊处理,为此ZGC设计了特殊的标记。另外,ZGC为了优化停顿时间,把一些需要在STW中并行处理的任务并发运行,这都被设计成非强根的并发标记。
  5. 重置转移集合中的页面:第一次垃圾回收无须处理这一步。
  6. 回收无效的页面:在内存充足的情况下不会触发这一步。
  7. 并发选择对象的转移集合:转移集合中就是待回收的页面。
  8. 并发初始化转移集合中的每个页面:在后续重定位(也称为Remap)时需要的对象转移表就是这一步初始化的。
  9. 转移根对象引用的对象:该步需要STW。
    10.并发转移:把对象移到新的页面中,这样对象所在的老的页面中所有活跃对象都被转移了,页面就可以被回收重用。
    在这里插入图片描述
    这里还有一个问题,就是并发算法中明确的提到重定位阶段,但上面的步骤中并没有体现。在ZGC中并没有明确这一步,重定位实际上被合并到标记阶段中,即在标记的时候如果发现对象引用到老的地址,这时会先完成重定位更新对象的引用关系,然后再标记对象。
    在这里插入图片描述
    在并发执行的每一个阶段都可能有应用线程请求新内存,所以垃圾回收中如果发现页面中不包含活跃对象,此时页面并不会被回收,而是直接重用。

并发处理

并发处理概述

并发标记和并发转移的两个并发处理并不容易,主要问题是在垃圾回收标记对象或者转移对象的过程中,应用线程可能正在改变对象引用关系图,从而造成漏标错标或者漏转移错转移。

错标漏标的后果: 在垃圾回收的并发标记中,错标不会影响程序的正确性,只是造成所谓的浮动垃圾;但漏标会导致可达对象被当作垃圾回收,从而影响程序的正确性。
解决方式: 引入三色标记法,区分对象的不同状态。
三色标记法: 是逻辑上的抽象,将对象用白色、灰色、黑色标记。白色表示还没被回收器标记的对象;灰色表示自身已经被标记到,但其拥有成员变量引用到别的对象还没有处理;黑色表示自身已经被标记到,且对象本身所有的成员变量引用到的对象也已经被标记。
漏标的情况: 黑色对象插入白色对象的新引用,当已经被标记的对象插入一个新引用时,新白色对象引用不会被标记成黑色;灰色对象删除到白色对象的引用,当在标记过程中删掉白色对象,那灰色对象不会变成黑色对象而漏标。
针对漏标的情况的两种实现方式:

  • 增量更新算法关注对象引用插入,把被更新的黑色或者黑色对象标记成灰色。
  • SATB关注引用的删除,即在对象被赋值前,把老的引用对象记录下来,然后根据这样对象为根重新标记一遍。

如下图,将对象2的成员变量设置成NULL,对象1的成员变量设置成对象3
在这里插入图片描述
增量更新算法: 将对象1新指向对象3的对象1变为灰色,对象1 的成员变量重新进行标记在这里插入图片描述
SATB: 将对象2删掉对象3的引用,那对对象3进行标记为灰色。
在这里插入图片描述
ZGC使用的是SATB算法,把所有访问到的数据都作为活跃对象标记或者转移。ZGC采用了地址多视图映射加快了对象的标记和转移实现SATB算法。

ZGC并发处理算法

  1. 初始化阶段
    在ZGC初始化之后,此时地址视图为Remapped,程序正常运行,在内存中分配对象,满足一定条件后垃圾回收启动,此时进入标记阶段。

  2. 标记阶段
    第一次进入标记阶段时视图为M0,在标记阶段,应用程序和标记线程并发执行,那么对象的访问可能来自标记线程和应用程序线程。
    标记线程: 标记线程访问对象就是对对象进行标记。它从根集合开始标记对象,在标记前先判断对象的地址视图是Remapped还是M0。如果是M0说明是标记阶段之后新分配的对象或者对象已经完成标记,说明对象是活跃的,无需处理。如果地址时图是Remapped,说明对象的前一阶段分配的,而且通过根可达,所以把对象的地址视图从Remapped调整为M0。
    应用程序线程: 应用线程在标记阶段如果创建新对象,则对象的地址视图为M0。如果应用程序访问的对象地址视图是Remapped,说明对象是前一阶段分配的,所以需要把该对象从Remapped调整为M0。
    所以在标记结束之后,对象的地址视图要么是M0,要么是Remapped。如果对象的地址是M0,说明对象在标记阶段被标记或者是新创建的,是活跃的;如果对象的地址视图是Remapped,说明对象是不活跃的,内存可以被回收。
    在这里插入图片描述

  3. 并发转移阶段
    标记结束后进入转移阶段,此时地址视图再次被设置为Remapped。转移阶段会把活跃对象转移到新的内存中,并回收对象转移前的内存空间。在转移阶段,应用程序和转移线程并发执行,那么对象的访问可能来自转移线程和应用程序线程。
    转移线程: 如果对象在对象活跃信息记录表中并且对象为M0,则转移对象,转移后的地址视图从M0调整为Remapped。如果对象的地址视图为Remapped,说明对象已经被转移了,无需处理。
    应用程序线程: 应用程序如果创建新的对象,则对象的地址视图为Remapped。如果访问的对象不在对象活跃信息表中,则说明对象是新创建或者无需转移,无需额外处理。如果访问的对象在对象活跃信息表中,且对象的地址视图为Remapped,说明对象已经被转移,无需处理。如果遇到M0,则需要将视图调整为Remapped。
    而对象视图M0、M1是为了区分是否是本地标记的视图还是上次标记的视图。
    M1: 本次垃圾回收中识别的活跃对象。
    M0:前一次垃圾回收的标记阶段被标记过的活跃对象,对象在转移阶段未被转移,但是在本次垃圾回收中被识别为不活跃对象。
    Remapped:前一次垃圾回收的转移阶段发生转移的对象或者是被应用程序线程访问的对象,但是本次垃圾回收中被识别为不活跃对象。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值