golang的GC(三色标记法+混合写屏障)学习笔记
一、标记清除法
在go1.3之前,go的GC是要先执行STW,然后给可达对象和不可达对象做上标记,然后再把不可达对象全部删掉。使用STW会使程序停止运行一定的时间,影响用户的使用,发生抖动。
二、三色标记法:
在go1.5之后出现了三色标记法,即扫描栈上和堆上的对象,没有被扫描到的对象为白色,第一次扫描会将root set,也就是根节点置为灰色,第二次遍历时,遍历的是灰色节点,此时会将灰色节点下面的可达节点置为灰色,遍历到的灰色节点变为黑色。最后遍历完成时,只剩下白色和黑色,白色对象将会被回收
如果三色标记法没有stw的话,在程序的运行过程中,可能会有堆上的黑色节点引用栈上的白色节点,栈上的这个白色节点原本是被栈上的灰色节点所引用,但是接着又删除掉了,这时,由于GC不会再遍历黑色节点下面的白色节点了,但是这个白色节点又是被引用的,这样就导致一个被引用的对象被回收掉了。
为了解决这个问题,引入了强三色不变式和弱三色不变式
强三色不变式: 不允许黑色对象引用白色对象
弱三色不变式:允许黑色对象引用白色对象,但这个白色对象必须被灰色对象引用,或者这个白色对象的最上层是个灰色对象。
插入写屏障(即实现了强三色不变式):
如果一个对象被引用,那么这个对象就会变为灰色对象
缺点:为了不耽误栈的运行效率,栈空间不会使用插入写屏障,所以在最后需要将栈上的对象全部置为白色并进行stw防止黑色对象再引用其他对象。这种情况还是需要短暂的stw
删除写屏障(即实现了弱三色不变式):删除一个对象,如果这个对象是灰色或白色,那么会将这个对象置为灰色
缺点:被删除的对象会被置为灰色,最后可能没有对象引用,这时就需要再对这些对象进行多一轮的扫描
go1.8之后的GC:三色标记法+混合写屏障
这个版本的垃圾回收机制还是需要STW,不过它的时间是微妙级别的,可以忽略不计,因为这个STW是在完成标记并进行最后收尾的时候执行的,所以时间很短。
结合插入写屏障和删除写屏障,诞生混合写屏障,它的步骤如下:
1、在一开始就将栈上的可达对象全部置为黑色,以及将新增的对象置为黑色
2、插入一个新对象,新对象变为灰色
3、删除一个新对象,新对象变为灰色
总结
在go的不同版本中使用的垃圾回收机制是不一样的,go1.3之前使用的是标记清除法,这个机制会有较长时间的STW,对程序运行会造成比较大的影响;go1.5后的版本采用的是三色标记法,首先会将rootset的节点置为灰色,然后再遍历灰色节点下的白色节点,如果白色节点可达,那么将白色节点置为灰色,将原本的灰色节点置为黑色,在没有STW的情况下,三色标记法出现一种情况,黑色节点引用白色节点,而这个白色节点是被灰色节点引用的,接着灰色节点又删除了白色节点,导致白色节点无法被遍历到并置为灰色,最终导致对象被回收,为了解决这个问题引入了强三色不变式和弱三色不变式来阻止以上情况的发生,最后是采用插入写屏障和删除写屏障来实现强弱三项不变式。go1.8之后的GC是使用三色标记法+混合写屏障机制来实现的,通过这几个步骤来操作:
1、刚开始垃圾回收时,将栈空间的对象全部置为黑色
2、在栈空间上创建的对象默认置为黑色
3、删除对象时,将对象置为灰色
4、插入对象时,将对象置为灰色
在栈空间上不开启屏障,在堆空间上开启混合写屏障。对于一些没有被根节点引用的黑色节点,最后会在下一轮扫描中被回收