Go的所有实现版本都没有单独用过删除写屏障,GoGC的历次迭代为标记清楚-插入写屏障-混合写屏障,且插入写屏障和混合写屏障对栈上都不操作。
GC,Garbage Collection,垃圾回收机制。 在Golang版本更迭中,GC的实现方法也经历了几次大的改进,分别是Go V1.3、Go V1.5以及Go V1.8三个版本。 注:文章中的图片均引自Golang中GC回收机制三色标记与混合写屏障
假设当前某个程序与对象之间的引用关系如下图所示,箭头表示某个对象引用另一个对象:
这时,如果触发GC操作,首先第一步会执行一个STW暂停,并将全部的可达对象进行一个标记。
而没有被标记的对象(对象5和对象6),说明当前程序已经不需要它了,因此回收。回收之后。STW暂停就结束了,程序恢复。
三色标记法在GC中统计三个表,分别是White白色标记表、Grey灰色标记表以及Black黑色标记表。
注:我们的目标是灰色节点集合为空,只有白色和黑色节点,回收白色节点,因此还要继续遍历
整体的三色标记是没有加STW的 在没有STW的情况下,三色标记法最不希望发生的事:
上面的两种情况同时发生会导致黑色对象引用的白色对象被回收,造成错误。
但是STW的过程有明显的资源浪费,对所有的用户程序都有很大影响。
如何能在保证对象不丢失的情况尽可能的提高GC效率,减少STW时间呢?
破坏两个条件同时成立!
即强制性的不允许黑色对象引用白色对象来破坏条件1
黑色对象可以引用白色对象,但白色对象存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象 来破坏条件2
在三色标记中如果满足强/弱之一,即可保证对象不丢失
采用屏障机制来实现强三色/弱三色……
屏障:在我们程序执行的某个流程中,可以额外的在中间加一层判断机制。如图所示:
思想:在不打扰正常业务的流程的情况下,做一些额外的判断。 屏障机制有插入屏障和删除屏障:
具体操作:在A对象引用B对象的时候,B对象被标记为灰色。(将B挂载A的下游,B被标记为灰色)
满足:强三色不变式。(不存在黑色对象引用白色对象的情况了,因为白色会强制变成灰色) 场景:
// A 之前没有下游,新添加一个下游对象B, B被标记为灰色
A.添加下游对象(nil, B)
// A 将下游对象C 更换为B, B被标记为灰色
A.添加下游对象(C, B)
注:为了不影响性能,插入屏障不在栈上使用。
然后停止STW,再做清除。
插入写屏障的不足: 结束时需要STW来重新扫描栈,大约需要10~100ms
具体操作:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。 满足:弱三色不变式。(保护灰色对象到白色对象的路径不会断) 场景:
// A对象,删除B对象的引用。 B被A删除,被标记为灰(如果B之前为白)
A.添加下游对象(B, nil)
// A对象,更换下游B变成C。 B被A删除,被标记为灰(如果B之前为白)
A.添加下游对象(B, C)
对象5已经被对象1删除,但是为什么没有回收呢?
删除写屏障的不足: 回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
删除写屏障也有一个STW的过程。