go/0013-GC
标记清除、三色并发标记清除、混合写屏障、强弱三色不变式
黑色:根对象 灰色:被引用的对象 白色:需要被清除的对象
问题:
并发标记会引来:漏标、多标问题。
为了解决漏标问题,引出了强弱三色不变式。
漏标:黑色对象引用了一个白色对象。影响很大,这个白色对象会被清除。 多标:一个灰色对象本应被标记为白色,而在这一次 GC 过程中没被标记的。影响不大,下一轮 GC 会清除。
强三色不变式: 黑色对象不可以引用白色对象。 弱三色不变式: 黑色对象可以引用白色对象,但是这个白色对象必须存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象。
为了遵循上述的两个方式,GC算法演进到两种屏障方式,他们是插入屏障和删除屏障。
插入屏障:
插入屏障的具体操作是,在A对象引用B对象的时候,B对象被标记为灰色(将B挂在A下游,B必须被标记为灰色)。
伪代码:
添加下游对象(当前下游对象slot,新下游对象ptr) {
// 第一步
标记灰色(新下游对象ptr)
// 第二步
当前下游对象slot =新下游对象ptr
}
插入屏障实际上是满足强三色不变式(不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)。
黑色对象的内存槽有两种位置:栈和堆。栈空间容量小,响应速度快,因为函数调用弹出频繁使用,所以“插入屏障”机制在栈空间的对象操作中不使用,而仅仅使用在堆空间对象的操作中。
当这一次全部三色标记扫描之后,栈上有可能依然存在白色对象被引用的情况(如上图的对象9)。 所以要对栈重新进行三色标记扫描,但这次为了对象不丢失。但这次扫描要启动STW暂停,直到栈空间的三色标记结束。
对这些白色对象启动STW暂定保护起来,那么任何并行对以上被保护的对象进行任何读写操作均会被拦截且阻塞,防止外界干扰。
堆空间的对象将不会触发STW,这样也是为了保证堆空间的GC回收性能
删除屏障:
删除屏障的具体操作是,被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。
删除屏障实际上是满足弱三色不变式,目的是保护灰色对象到白色对象的路径不会断
GC 的阶段与混合写屏障的配合
Go GC 分为四个阶段:
- Mark Setup(STW):开启写屏障,准备标记。
- Marking(并发):GC 与用户程序并发运行,使用混合写屏障跟踪对象变化。
- Mark Termination(STW):关闭写屏障,完成标记,统计垃圾对象。
- Sweeping(并发):并发清除垃圾对象,用户程序可同时分配新内存。