三色标记(可达性分析算法)及一些思考

本文探讨了Java垃圾回收中的三色标记算法在并发环境中的问题,涉及黑色对象与白色对象引用变化的处理,介绍了增量更新(CMS)和原始快照(G1)的解决策略,以及可能出现的浮动垃圾和对象消失情况。

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

三色标记算法及一些思考

最近,在阅读《深入理解Java虚拟机》一书关于可达性分析内容的描述后,有些感悟,故写下此文章。

首先,我们假设根节点已经枚举完毕,并以该根节点为起点对Java对象进行可达性分析,

三色标记算法

在可达性分析的过程中,先约定好三种颜色的含义,按照“是否访问过”进行划分:

  • 白色:对象未被垃圾回收器访问过。在垃圾回收结束之后,对象仍然为白色,即表示不可达。
  • 黑色:对象及其所有引用都已被扫描过,是安全可存活的。
  • 如果由其它对象的引用指向黑色对象无需重新扫描。
  • 黑色对象不可直接指向白色对象。
  • 灰色:对象已被访问过,但这个对象上至少存在一个引用还没有被扫描。

那么假设单线程执行(此时用户线程都被挂起),执行顺序如图所示,这个过程是没啥问题的。
在这里插入图片描述
(该图来源于《深入理解Java虚拟机》P88)

并发下导致的问题

如果用户线程和垃圾回收的线程并发执行,那么可能会有如下的执行顺序:
在这里插入图片描述

阅读这部分内容的时候,一直在纠结一个点,前文提到黑色对象不可直接指向白色对象的,这里又说用户线程将白色对象与黑色对象建立了引用关系,两者之间是否矛盾?后边想了想,前者说的【黑色对象不可直接指向白色对象】应该是指垃圾回收线程正常执行的情况下,无用户线程并发修改引用的场景。

即用户线程和垃圾回收线程并发执行时会带来两种可能性:

  • 原本应该消失的对象在可达性分析之后存活了。(浮动垃圾,可容忍,大不了下次清理)
  • 原本应该存活的对象在可达性分析之后消失了。(0容忍)

对此,书中也给出了对象消失的先决条件(两者需要同时成立):

  • 赋值器插入了一条或多条从黑色对象到白色对象的新引用;
  • 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用

解决方式

知道对象消失的先决条件,那么解决方式只要破坏其中一条即可:

  • 增量更新(CMS)
    • 当赋值器插入了一条或多条从黑色对象到白色对象的新引用时,将该引用记录下来,在并发可达性分析结束之后重新扫描。
  • 原始快照(G1)
    • 按照一开始扫描的对象图进行搜索,如果在这个过程中,赋值器删除了全部从灰色对象到该白色对象的直接或间接引用,将该引用记录下来,在并发可达性分析

这里阅读时也有个疑问,如果采用原始快照的方式,在可达性分析的过程中,用户线程新增了一个对象,并将黑色对象指向白色对象,这个时候如何处理?这里就要关注一个点了,【原始快照】即一开始扫描的对象图,在可达性分析的过程中,新增加的对象理所当然不在该对象图中。

### JVM三色标记算法的原理 三色标记算法是一种用于JVM垃圾回收(GC)过程中的增量式标记算法,其主要目的是通过减少全局暂停时间(即 Stop-The-World, STW),提升系统的响应性吞吐量。该算法的核心思想是将堆中的对象按照不同的状态划分为三种颜色:白色、灰色黑色。 #### 颜色定义及其含义 1. **白色对象** 表示尚未被访问到的对象,可能属于不可达对象集合的一部分。如果最终仍为白色,则会被视为垃圾并清除[^1]。 2. **灰色对象** 表示已经被发现但还未完成对其引用字段扫描的对象。这些对象本身已被标记,但它们所指向的其他对象还需要进一步处理[^2]。 3. **黑色对象** 表示已经完全扫描过的对象,不仅自身被标记为其可达,而且它的所有直接引用也均已完成扫描[^1]。 #### 工作流程概述 三色标记算法的工作可以分解成以下几个方面: 1. 初始化阶段,所有的对象都被初始化为白色。 2. 将根节点(Root Set)加入队列,并将其染成灰色。 3. 不断从灰名单中取出一个对象进行扫描: - 如果此对象有引用指向白色的子对象,则将这些子对象变为灰色,并放入待处理列表; - 当前正在处理的对象一旦完成了全部引用域的遍历之后就变成黑色[^2]。 当不再存在任何灰色对象时,说明整个图结构上的可达性分析结束,此时剩下的白色对象即可判定为垃圾可予以销毁。 #### 实现细节与挑战——并发模式下的问题解决方法 尽管三色标记法理论上简单明了,但在实际应用过程中特别是在并发环境下会遇到一些棘手的问题比如“悬挂指针”现象。“悬挂指针”的情况是指某个原本应该成为垃圾的目标突然又被重新赋值给新的有效引用路径下从而造成遗漏回收的风险。为了应对这种情况通常采用读屏障(Read Barrier)或者写屏障(Write Barrier),其中后者更为常见因为效率较高且易于维护。 ##### 写屏障的作用机制 在引入写屏障的情况下每当程序修改某条记录之前都会先检查这条记录是否涉及到了跨代复制或者其他特殊操作必要的话则立即更新相应的元数据信息确保不会漏掉任何一个新创建出来的潜在存活实例即使是在多线程交错执行期间也能保持一致性约束条件满足[^1]。 ```java // 示例伪代码展示如何利用写屏障来辅助实现三色标记算法 public void writeObjectReference(Object oldRef, Object newRef){ if (isInOldGeneration(oldRef)){ recordRememberedSetEntry(newRef); // 记录老年代至新生代间的引用关系变动 } } ``` ### 总结 综上所述,三色标记算法作为现代高效垃圾收集器的基础技术之一,在降低停顿时间增强实时性能表现等方面发挥了重要作用。通过对不同生命周期状态下对象的颜色编码以及合理运用诸如写屏障这样的工具手段成功克服了许多传统一次性全盘扫描所带来的弊端实现了更加灵活精准的记忆体资源调配策略[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

legendaryhaha

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

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

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

打赏作者

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

抵扣说明:

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

余额充值