golang混合写屏障实现

  1. 写屏障只应用在堆上应用,栈上部启用。

  2. GC开始将栈上的对象全部扫描并标记为黑色。

  3. GC期间,任何在栈上创建的新对象,均为黑色。

  4. 被删除的对象标记为灰色。

  5. 被添加的对象标记为灰色。

golang gc流程

  1. STW(StopTheWorld)开启写屏障,记录数据段以及栈中roots的必要信息

  2. StartTheWorld开始标记,此时mutator(用户程序)和GC标记并发执行

  3. 标记完成再次STW,关闭写屏障

  4. StartTheWorld进入轻扫阶段


其中标记阶段有一个问题很多文章没有解释清楚,就是,栈上的黑色对象是否会引用到一个堆上的白色对象。因为栈是没有写屏障的,如果栈上的黑色对象引用了堆上的白色对象,被引用的白色对象就会被GC错误回收。
例如下图所示的场景:


A如果引用了D,同事B删除了对D的引用,那么D不就被错误的回收了吗。

实际这种情况时不会发生的:

  1. 对于某个goroutine的栈扫描是原子的操作,会暂停此goroutine,标记结束后栈对象全部为黑色。
  2. 已被扫黑的栈,引用的堆上的对象至少是灰色。(比如C对象)。所以不可能发生同栈下引用改变会影响GC的问题,因为白色对象都在保护之中。
  3. 不可能发生上述的跨栈的引用。因为“对象不是从天上掉下来的”。因为A没有路径能够抵达D。
  4. GC期间栈上新建的对象全是黑色,即使逃逸到堆上,也是黑色,所以A如果引用了新创建的对象也会是黑色,而不会发生错误的回收

参考:
golang 垃圾回收(五)深入剖析混合写屏障
Golang三色标记+混合写屏障GC模式全分析
go: GC时写屏障与栈的引用变化