Golang GC 三色标记和混写屏障

April 20, 2023 1:53 PM

三色标记

三色标记就是通过三个阶段来标记需要清除的对象都有哪些,具体的过程如下:

  • 每次创建的新对象,默认颜色都为白色
  • 每次GC回收开始,会从根节点开始遍历所有对象,把遍历到的对象从白色集合中放入灰色集合
    • 非递归,之遍历一次
  • 遍历灰色集合,将灰色集合对象的引用对象从白色集合放到灰色集合,之后将此灰色集合放入黑色集合。
  • 重复上一个步骤,知道灰色集合中无对象
    • 遍历完成后,仅剩两种对象,黑色(待保留)和白色(待清除)
  • 回收所有标记为白色的对象,进行垃圾回收

为了保证在并发情况下,GC过程中数据安全,我们在三色标记之前会加上STW

不启用STW会出现的问题

  • 一个白色对象被黑色对象引用(白色被挂在黑色下)
  • 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色同时丢了白色)

满足其中一个条件,就会出现对象丢失现象。

屏障机制

我们让GC回收器,满足下面两种情况之一时,即可保留对象不丢失。

  • 强三色不变式,不允许黑色对象引用白色对象
  • 弱三色不变式,所有被被黑色对象所引用的白色对象都处于灰色保护状态

为遵循上述两个方式,GC 算法演进了两种屏障方式,分别是“插入屏障”和“删除屏障”

屏障机制仅使用与在堆区,栈去不触发

插入屏障

在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下面,B必须被标记为灰色),满足强三色不变式

删除屏障

被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。满足弱三色不变式

回收精度低,要在下一轮GC中才能被清除掉

Go V1.8混合写屏障

插入些屏障和删除写屏障的缺点:

  • 插入写屏障,结束时需要STW来重新扫描栈
  • 删除写屏障,回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。

混合写屏障,结合两者优点。

混合写屏障规则

  • GC 开始将栈上的对象全部扫描并标记为黑色(之后栈上不在二次扫描,无需STW)
  • GC期间,任何栈上创建的新对象,均为黑色
  • 被删除的对象标记为灰色
  • 被添加的对象标记为灰色

满足了变形的弱三色不变式

总结

  • Go V1.3 - 普通标记清除法,整体过程都需要STW,效率极低
  • Go V1.5 - 三色标记法,堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
  • GoV1.8 - 三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高