1,4
1-2-3
4-7
5,6

一、go1.3

mark and sweep

1、start stw

2、执行全量扫描并标记

3、清理掉无用的对象

4、stop stw

二、go1.5

开始引入三色标记法

白色(垃圾),灰色(中间状态),黑色(非垃圾)

1、刚开始将全部节点标记为白色

2、开始遍历,只遍历一层(不是递归深度遍历),找到第一层就可达的节点,也就是 1,4,将他们从白色移到灰色。

3、分别遍历1和4,遍历一层,得到2和7,将2和7从白色移到灰色,将1和4从灰色移到黑色。

4、接着遍历灰色表中的节点,也就是2和7,于是2和7被移到了黑色,3被移到灰色。

5、接着遍历灰色表,也就是3,没有子节点,将3移到黑色。

6、遍历完成,最终只剩白色(未遍历)和黑色(已遍历过了)。
在这里插入图片描述

在整个过程中,是没有开启STW,那么这必然会导致程序混乱,比如,在完成了步骤5之后,突然程序中出现了4对5的引用,于是需要遵循两个规则。

1、强三色不变式

处于黑色表的对象不允许去引用处于白色表的对象。

2、弱三色不变式

黑色可以引用白色,前提是,同时还有灰色对象直接引用该白色对象,或者可达的链路上游存在灰色对象,即确保此白色对象不被清理。

那么,这两个逻辑要怎么实现呢?于是就有了屏障机制(也就是Hook函数)。

1、为了满足强三色不变式,增加了插入屏障:当对象A引用对象B的时候,对象B需要被标记为灰色。如果对象B在堆上,那么就执行此操作;如果对象B在栈上,那么就不执行此操作,这是为了保证栈的速度,也就意味着对象B还是白色,那么,在清理工作之前还是需要在栈上开启STW扫描一遍,确保B最终也是黑色的,由于栈空间不大,这大概需要10-100毫秒,最后才去执行清理工作。

2、为了满足弱三色不变式,增加了删除屏障:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。这样会导致回收精度低,一个对象即使被删除了,最后一个指向它的指针依然可以活过这一轮,在下轮GC中被清理掉。

三、go1.8

在三色标记法的基础上增加了混合写屏障

上面的插入屏障和删除屏障都或多或少的有点缺点,因此需要进一步优化,引入了混合写屏障。

具体操作

1、GC开始,将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW)。

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

3、被删除的对象标记为灰色。

4、被添加的对象标记为灰色。添加和创建意思不一样,添加的意思是该对象已经存在,只是添加到某个节点的下方。