概要
三色标记法是运用在GC并发标记过程中的算法,被扫描的对象们被分到三种集合里:
- 白色集合:还没被标记 or 没有被标记到(这个就可以认为是垃圾了)
- 灰色:标记到了,但是字段还没被标记完。
- 黑色:标记完了。
所有标记完成后,会把白色的作为垃圾回收掉(因为不可达)。
步骤
- 创建:白、灰、黑 三个集合。
- 将所有对象放入白色集合中。
- 从GC Root开始遍历所有对象,把遍历到的对象从白色集合放入灰色集合(备注:这里放入灰色集合的都是GC Root的对象)。
- 遍历灰色集合,将灰色对象引用的对象(其实就是灰色对象的字段)从白色集合放入灰色集合,然后将分析过的灰色对象(所有字段都处理完毕的)放入黑色集合。
- 直到灰色中无任何对象。
- 通过写屏障(Pre-Write Barrier)检测对象有变化,重复以上操作(备注:因为 mark 和用户程序是并行的,所以在上一步执行的时候可能会有新的对象分配,写屏障是为了解决这个问题引入的)。
- 回收掉所有白色对象(垃圾)
关于Pre-Write Barrier:
可以看到三色标记法有很多步骤,而这些步骤是和用户线程并发运行的,也就是说在标记过程中,用户还在创建新对象,或者抛弃老对象。
先讲创建新对象的情况:
- A对象已经被标记为黑色
- 用户线程:A.field = new X()
这种情况下,X是白色的,而且按照三色标记法的规则,黑色的A是不会再次被标记的。如果不能把X变成灰色,那么它就会被垃圾回收掉,这个是是存在问题的。
因此,在标记开始之后,需要在对象引用更新的地方添加一个Pre-Write Barrier,用来将X直接标记为灰色。
注意:Pre-Write Barrier和Post-Write Barrier作用的对象不同,前者是针对三色标记算法的缺陷,后者是针对Card Table。
参考资料
-
Go垃圾回收之三色标记算法
-
Java Hotspot G1 GC的一些关键技术
原文
https://chanjarster.github.io/post/jvm/tri-color/