type
status
date
slug
summary
tags
category
icon
password
创建时间
Feb 28, 2025 07:40 AM
看懂本文,需要先了解“可达性分析”。
原理
三色标记法,顾名思义,我们使用三种颜色进行标记,即黑、灰、白。三种颜色代表的含义分别如下:
- 黑:已经被标记完成,且依旧存活的对象。
- 灰:当前对象已经被标记完成,但关联节点(引用的对象)还未标记的对象。
- 白:未曾标记过的对象,或不具备引用的对象(垃圾对象)。
执行过程

- 初始状态:在启动时,分别创建:黑、白、灰三个集合。一开始除了 GCRoots 外,所有的对象都在白色集合中;
- 初始标记:发生短暂的 STW,将所有与 GCRoots 直接相连(直接可达)的对象转入灰色集合中;
- 并发标记:从灰色对象集合出发,根据可达性分析算法,依次标记其所引用(包含)的子对象。当所有子对象都被标记完了之后,这些灰色对象被转入到黑色对象集合中;不断重复该操作,直至灰色集合没了元素为止。
- 误标纠错(重新标记):标记完成所有对象后,再次触发 STW,通过 write-barrier(写屏障检)测对象是否有变化,如果发生了改变则重新标记,纠正并发标记期间的“误标”;
- 并发清除(垃圾清理):将白色集合中的所有对象全部回收
- 标记复位:并发清除完成后,把标记复位,将所有的对象再次放入白色集合中,等待迎接下次 GC。
三色标记算法存在的问题
由于在并发标记阶段,GC线程和用户线程是并发运行的,随时可能发生对象之间引用变化从而导致“多标”和“漏标”的问题。——针对“黑色节点”的
多标
多标:又称“错标”、“误标”,是指被标记的黑色对象中,突然断开了对另一个对象的引用,导致另外一个原本已经被标记为黑色的对象突然变为了垃圾。但是因为该对象已经被标记了,所以收集器不会对该对象进行再次标记,而等到清除工作发生时,因为当前这个对象在最初是被标记为了黑色,所以收集器也不会回收它。
这样的黑色对象,被称为并发标记产生的浮动垃圾。这种情况问题不大,之后的 GC 迟早会清理掉。
因为程序的原因,频繁出现这种清理,加上内存紧张,这种情况如何解决呢?
漏标
相比之下,漏标的严重性就比较大了。漏标是指重新建立引用的白色对象“父节点”已经被标记黑色了,GC线程不会再次标记该对象以及其成员对象,所以这些白色对象会被一直停留在白色集合中。最终导致的结果就是这些依旧存在引用的存活对象会被“误判”为垃圾对象清除掉。
有以下两种情况:
- 一条用户线程在执行过程中,断开了一个未标记的白色对象连接,然后该对象又被一个已经标记成黑色的对象建立起了引用连接。

- 一条用户线程在执行过程中,正好在GC线程标记时,将一个灰色对象与一个未标记的白色对象之间的引用连接断开了,然后当GC标记完成这个灰色对象,将其标记为黑色后,之前断开的白色对象又重新与之建立起了引用关系。
- 灰色对象断开了与白色对象的引用(直接引用或间接引用都可)——灰色对象的包含的对象引用发生了变化;
- 已经标为黑色的对象重新引用了白色对象——黑色对象增加了新的引用;

漏标发生,需要同时满足的两个条件:
如何解决漏标问题呢?
下次在记录,本次对三色标记法的基本知识进行学习。