回看Golang的发展历程,作者都在提升Go执行效率上下足功夫,从Go的GC发展可见一斑。v1.3版本时,使用的是标记清除法回收对象,其缺点是在回收时需要STW(Stop The World)。STW是影响Go的GC性能的重要因素,减少或者消灭STW是GC优化道路上的重要使命。
三色标记法不使用STW
使用之前本人所写的文章
的图片继续讲解。三色标记法不使用STW的时候有可能会出现以下描述的情况。

标记为黑色的对象是被保留不会释放内存的;标记为灰色是过渡状态的对象,会被扫描;标记为白色的对象会被GC清除释放内存的。GC已经扫码完对象1和对象2,标记为黑色,并发现它们的引用,对象2和对象5,并标记为灰色。
标记为灰色的对象2,有指针p指向白色对象3 。程序还在运行当中,还没扫描到对象2,已经标记为黑色的对象4此时创建指针q指向对象3。虽然对象4已经被扫描标记为黑色,但是还有对象2没被扫描,依然有可以正常保护对象3不被回收。

再变动一下,对象2把指向对象3的指针p删除,那么对象3仅仅挂在已经被扫描并标记为黑色的对象4下。

按照三色标记法,对象2和对象5会标记为黑色,而对象3,由于对象4已经被扫描过了,所有只能还是原来的白色标记,等待被清理。这不是我们希望的结果。这种情况的出现是由于我们放弃使用STW机制保护导致的。

根据上面的情况分析,在没有启用STW的情况下,三色标记法如果满足以下两个条件就会有对象被无辜清理:
- 条件一:一个白色对象被黑色对象引用
- 条件二:灰色对象与此白色对象之间的可达关系遭到破坏
为了避免无辜对象被清理,最简单的方式是STW,但是STW时间过长会对用户体验造成影响,那如何在保证不丢失对象的情况下提高GC效率,减少STW的时间呢?
强弱三色不变式
为了解决上面的问题,Go开发者给出的答案是强弱三色不变式。下面分别介绍强弱三色不变式。
强三色不变式
规则:强制性的不允许黑色对象引用白色对象
此规则破坏了条件一。
条件一:一个白色对象被黑色对象引用
弱三色不变式
规则:黑色对象可以引用白色对象,白色对象存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象
此规则破坏了条件二。
条件二:灰色对象与此白色对象之间的可达关系遭到破坏
下面的图例可以帮忙理解弱三色不变式。情况1是不被允许,情况2和情况3时被允许的。黑色所引用的白色对象始终会被灰色对象引用,不管是直接引用还是间接引用。

因此,在三色标记的算法中,只要满足强/弱三色不变式的其中一种,即可保证对象不被丢失。具体如何实现,请看下一篇文章《Golang垃圾回收简明教程3--插入删除写屏障机制》。
晚安~