[读书笔记]深入理解Go语言——GC

178 阅读4分钟

Go V1.5 的三色标记法

本文是刘丹冰的《深入理解Go语言》第2章的笔记

作者电子版资料

三色标记法

  1. 新创建的对象默认为白色
  2. 每次 GC 从根节点遍历对象,将遍历到的对象放入灰色集合
  3. 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,然后将灰色对象放入黑色集合
  4. 重复 3. 直至灰色集合无对象

此时内存中只有黑色和白色对象。黑色对象为程序逻辑可达的对象,白色对象为不可达的垃圾,目前程序逻辑不依赖它们,需要被清除

  1. 回收所有白色对象,即回收垃圾

因为执行并发流程的内存可能互相依赖,程序逻辑也会在标记-清除的过程中改变对象引用关系,导致错误回收合法对象,所以这种 GC 策略必须在三色标记前加上 STW ,确定黑白对象之后再放开 STW

触发三色标记法不安全的必要条件

  1. 一个白色对象被黑色对象引用(白色挂在黑色下)
  2. 灰色直接可达的白色对象的引用关系被破坏(灰色同时丢失该白色)

以上两个条件同时满足就会导致对象丢失,进而错误地回收垃圾。

已经被标记为黑色的对象不会再被扫描,那么它所引用的对象也没有被扫描的可能

黑色对象引用白色对象,该白色对象不可能从来自黑色的对象的路径遍历改变颜色

这个白色对象能够被改变颜色的机会只能来自它上游的灰色对象,如果在此时这个白色对象上游直接或间接引用的灰色对象不再可达该白色对象,那么该白色对象就失去了所有改变颜色不被回收的机会,它和其下游的白色对象都会被一起错误地清理掉。

image.png

image.png

image.png

image.png

屏障机制

只要破坏上面的两个必要条件之一旧客户确保对象不丢失,破坏这两个条件的情况就是强三色不变式和弱三色不变式。

强-弱三色不变式

  1. 强三色不变式:不存在黑色对象引用到白色对象的指针(不允许黑色对象引用白色对象就杜绝了白色对象被误删的可能)
  2. 弱三色不变式:所有被黑色对象引用的白色对象都处于灰色保护状态(黑色对象可以引用白色对象,但是白色对象必须被灰色对象直接或间接引用,链路上游的灰色对象可以保护白色对象)

image.png

image.png

image.png

插入屏障

插入屏障实际上满足了强三色不变式

具体操作: A对象引用B对象时,B对象被标记为灰色(A对象只要存在下游对象,必定会被标记为灰色)

黑色对象的内存槽有两种位置:栈和堆。

栈容量小但是要求速度快,函数调用会被频繁使用,因此插入屏障在栈空间不使用。

插入屏障在堆上使用后,不会立即垃圾回收,而是在栈上STW,将栈上对象重新标记为白色,然后再次进行三色标记扫描,直到扫描结束关闭STW

最后将堆和栈空间中扫描剩余的白色节点回收清除,这次栈空间的STW时间大约10~100 ms

缺点: 结束时要STW重新扫描栈,标记栈上引用的白色对象的存活

删除屏障

删除屏障实际上满足了弱三色不变式

具体操作:被删除的对象,自身为灰色或白色,则被标记为灰色(父对象断开对子对象的引用时,如果子对象为灰色或白色,要把子对象标记为灰色)

子对象被标记为灰色,相当于灰色的父对象“抛弃”子对象前给子对象一个“免死金牌”,防止白色的子对象在程序运行过程中被黑色对象引用(小白明明在替条子当卧底,却没有灰色上级证明小白是良民)

缺点:回收精度较低,一个应当被删除的对象及其下游的子对象 ,因为在被删除前被标记为了灰色,所以在遍历的最后会变成黑色,可以“活”过这一轮垃圾回收

灰色上级给小白作保是良民,但是小白没有给其他领导干活,过了一轮GC的时间,没人知道小白是良民,他就只能下辈子再做个好人了

Go V1.8 的混合写屏障

规则

  1. GC 开始将栈上对象全标记为黑色(之后不再重复扫描,无需STW
  2. GC期间,任何栈上创建的新对象均为黑色
  3. 被删除的对象标记为灰色
  4. 被添加的对象标记为灰色

后面的场景分析参考原作者原文