不是同一个概念,两者描述了垃圾回收中完全不同层面和目的的机制:
两次标记 (Two-Phase Marking for Finalization):
- 目的: 专门处理那些覆盖了
finalize()方法的对象,给它们一个“临终遗言”的机会。 - 过程:
- 第一次标记: GC 进行可达性分析,标记出所有不可达的对象(即从 GC Roots 无法访问到的对象)。
- 筛选: 在第一次标记出的不可达对象中,筛选出覆盖了
finalize()方法 且之前从未被放入过 F-Queue 的对象。 - 入队: 将这些筛选出来的对象放入一个名为 F-Queue 的特殊队列。
- 执行 Finalizer: JVM 会启动一个低优先级的 Finalizer 线程(注意:不是 GC 线程本身),去异步地、逐个地执行 F-Queue 中对象的
finalize()方法。这个执行过程不受 GC 控制,执行时间不确定,甚至可能让对象重新被引用(“复活”)。 - 第二次标记: 在下一次 GC 发生时(可能是 Minor GC 或 Full GC),GC 会再次进行可达性分析。
- 对于 F-Queue 中的对象,这次分析会检查它们:
- 如果执行完
finalize()后仍然不可达,则真正标记为可回收。 - 如果在
finalize()中被“复活”(重新与 GC Roots 建立引用链),则变成存活对象。
- 如果执行完
- 对于第一次标记就不可达且没有覆盖
finalize()方法 的对象,或者已经执行过一次finalize()的对象,这次标记会直接判定它们为可回收。
- 对于 F-Queue 中的对象,这次分析会检查它们:
- 核心: 围绕
finalize()方法展开,给特定对象一次“复活”的机会。是串行、分阶段的(第一次标记 -> 执行 finalize -> 第二次标记)。
- 目的: 专门处理那些覆盖了
三色标记 (Tri-Color Marking):
- 目的: 一种支持并发标记的算法思想。它允许垃圾回收器在用户线程(Mutator)不暂停(或者只短暂暂停)的情况下,安全地遍历对象图并标记存活对象,解决并发扫描时对象引用关系变化带来的问题。
- 原理:
- 白色 (White): 表示对象尚未被垃圾回收器访问到。在标记开始时,所有对象都是白色。标记结束时仍为白色的对象就是垃圾(不可达)。
- 灰色 (Grey): 表示对象已被垃圾回收器访问到,但该对象引用的其他对象还没有被完全扫描完。灰色对象是标记过程中的“前沿”,GC 需要从这些对象出发继续扫描。
- 黑色 (Black): 表示对象已被垃圾回收器访问到,且该对象引用的所有其他对象也都被扫描过了。黑色对象被认为是存活对象,不会再被重新检查。
- 过程 (并发标记阶段):
- 初始:GC Roots 直接引用的对象被标记为灰色,放入待处理队列。其他对象为白色。
- 并发标记:GC 线程(与用户线程并发执行)从灰色队列取出对象:
- 将该对象标记为黑色。
- 扫描该对象引用的所有其他对象:
- 如果被引用对象是白色,则将其标记为灰色并加入队列。
- 如果被引用对象是灰色或黑色,则跳过(已处理或正在处理)。
- 重复步骤 2 直到灰色队列为空。
- 核心问题与解决方案(增量更新/原始快照): 在并发标记过程中,用户线程会修改对象的引用关系(赋值),可能导致:
- 误标垃圾(浮动垃圾): 可以接受,下次 GC 回收。
- 漏标存活(对象消失): 绝对不允许。三色标记通过写屏障 (Write Barrier) 技术配合两种策略解决:
- 增量更新 (Incremental Update): 关注插入引用(
a.field = newObj,原来 a 是黑,newObj 是白)。当用户线程将黑色对象(a)的引用指向一个白色对象(newObj)时,写屏障会记录这个引用关系(例如将黑色对象 a 重新标记为灰色),强制 GC 重新扫描 a。 - 原始快照 (Snapshot At The Beginning - SATB): 关注删除引用(
a.field = null或a.field = otherObj)。GC 在开始时对整个对象图做一个逻辑快照。写屏障会记录被切断引用的旧对象(a.field原来指向的 oldObj),确保在标记过程中,即使引用被删除,这个旧对象(oldObj)在本次 GC 中仍然被视为存活(至少是灰色),避免其因为引用删除而错误地被回收。
- 增量更新 (Incremental Update): 关注插入引用(
- 核心: 一种并发标记算法,利用三色抽象和写屏障安全高效地在用户线程运行的同时完成对象图的标记。是并发、增量的。
总结关键区别:
| 特性 | 两次标记 (Finalization) | 三色标记 (Concurrent Marking) |
|---|---|---|
| 主要目的 | 处理 finalize() 方法,给对象“复活”机会 |
支持并发标记,减少 STW 停顿时间 |
| 核心机制 | 分两次 GC 扫描,中间执行 finalize() |
三色状态抽象 (白/灰/黑) + 写屏障 |
| 处理对象 | 覆盖了 finalize() 的不可达对象 |
所有对象 |
| 并发性 | 基本是串行的(Finalizer 线程异步但 GC 阶段需等待) | 核心目标是并发,GC 标记线程与用户线程并行 |
| GC 阶段 | 发生在标记阶段(与可达性分析强相关) | 本身就是标记阶段的核心算法 |
| 解决的问题 | finalize() 方法的特殊处理 |
并发标记期间对象图变化导致的对象消失问题 |
| 关联性 | 三色标记的并发标记阶段完成后,那些不可达(白色)的对象,如果覆盖了 finalize(),后续的处理流程(入 F-Queue, 执行 finalize, 第二次标记)就是“两次标记”所描述的过程。 |
简单来说:
- 两次标记 是关于
finalize()方法如何影响对象回收命运的一个特定流程。 - 三色标记 是现代垃圾回收器(如 CMS, G1, Shenandoah, ZGC)在标记阶段使用的一种底层算法,目的是高效、并发地找出所有存活对象。标记完成后,那些被判定为不可达(白色)的对象,如果它们有
finalize()方法,才会进入“两次标记”的流程。
因此,它们不是同一个东西,而是垃圾回收中不同层次、解决不同问题的机制。三色标记是更基础、更通用的并发标记技术,而两次标记是针对 finalize() 这个特殊特性的一个处理步骤。强烈建议避免使用 finalize(),因为它不可靠、性能差且可能导致问题。
