三色标记法
白灰黑
初始时,所有对象都为白色,
GC开始,开启SWT,遍历堆栈root,将直接可达的对象标记为灰色,
遍历灰色结点,将直接可达的对象标记为灰色,自身标记为黑色,
继续执行第三步同样的步骤,直到所有能够访问到的结点都被标记为黑色,
关闭SWT,回收所有白色标记的对象。
如果没有SWT,程序正常执行,可能会有如下的情况,导致对象被误当作垃圾回收。
白色对象本来被一个灰色对象引用,但是该灰色对象将该引用赋给了黑色对象,灰对白的引用断开。此时,由于不会对黑色对象的引用进行检测标记,即该白色节点即使被引用也无法被标记为灰色,最终当作垃圾处理掉。
三色标记法出现对象丢失,要满足以下两个条件:
条件一:白色对象被黑色对象引用
条件二:灰色对象与白色对象之间的可达关系遭到破坏
只要破坏两个中的任何一个不会导致对象丢失的发生。
如何破坏两个条件
强不变式: 不允许黑色对象引用白色对象
弱不变式: 黑色对象可以引用白色对象,但是白色对象必须直接或间接被灰色对象引用。(保证白色对象一定会被扫描到)
当一个对象引用另外一个对象时,将另外一个对象标记为灰色。
插入屏障仅会在堆内存中生效,不对栈内存空间生效,这是因为go在并发运行时,大部分的操作都发生在栈上,函数调用会非常频繁。数十万goroutine的栈都进行屏障保护自然会有性能问题。
如果一个栈对象 黑色引用白色对象,白色对象依然会被当作垃圾回收。
因此,最后还需要对栈内存 进行STW,重新rescan,确保所有引用的被引用的栈对象都不会被回收。
删除写屏障
当一个白色对象被另外一个对象时解除引用时,将该被引用对象标记为灰色(白色对象被保护)
缺点:产生内存冗余,如果上述该白色对象没有被别的对象引用,相当于还是垃圾,但是这一轮垃圾回收并没有处理掉他。
GC刚开始的时候,会将栈上的可达对象全部标记为黑色。
GC期间,任何在栈上新创建的对象,均为黑色。
将栈上的可达对象全部标黑,最后无需对栈进行STW,就可以保证栈上的对象不会丢失
堆上被删除的对象标记为灰色
堆上新添加的对象标记为灰色
go 1.3 之前采用标记清除法,需要STW
go 1.5 采用三色标记法,插入写屏障机制(只在堆内存中生效),最后仍需对栈内存进行STW
go 1.8 采用混合写屏障机制,屏障限制只在堆内存中生效。避免了最后节点对栈进行STW的问题,提升了GC效率