三色标记
三色标记就是通过三个阶段来标记需要清除的对象都有哪些,具体的过程如下:
- 每次创建的新对象,默认颜色都为白色
- 每次GC回收开始,会从根节点开始遍历所有对象,把遍历到的对象从白色集合中放入灰色集合
- 遍历灰色集合,将灰色集合对象的引用对象从白色集合放到灰色集合,之后将此灰色集合放入黑色集合。
- 重复上一个步骤,知道灰色集合中无对象
- 遍历完成后,仅剩两种对象,黑色(待保留)和白色(待清除)
- 回收所有标记为白色的对象,进行垃圾回收
为了保证在并发情况下,GC过程中数据安全,我们在三色标记之前会加上STW
不启用STW会出现的问题
- 一个白色对象被黑色对象引用(白色被挂在黑色下)
- 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色同时丢了白色)
满足其中一个条件,就会出现对象丢失现象。
屏障机制
我们让GC回收器,满足下面两种情况之一时,即可保留对象不丢失。
- 强三色不变式,不允许黑色对象引用白色对象
- 弱三色不变式,所有被被黑色对象所引用的白色对象都处于灰色保护状态
为遵循上述两个方式,GC 算法演进了两种屏障方式,分别是“插入屏障”和“删除屏障”
屏障机制仅使用与在堆区,栈去不触发
插入屏障
在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下面,B必须被标记为灰色),满足强三色不变式
删除屏障
被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。满足弱三色不变式
回收精度低,要在下一轮GC中才能被清除掉
Go V1.8混合写屏障
插入些屏障和删除写屏障的缺点:
- 插入写屏障,结束时需要STW来重新扫描栈
- 删除写屏障,回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
混合写屏障,结合两者优点。
混合写屏障规则
- GC 开始将栈上的对象全部扫描并标记为黑色(之后栈上不在二次扫描,无需STW)
- GC期间,任何栈上创建的新对象,均为黑色
- 被删除的对象标记为灰色
- 被添加的对象标记为灰色
满足了变形的弱三色不变式
总结
- Go V1.3 - 普通标记清除法,整体过程都需要STW,效率极低
- Go V1.5 - 三色标记法,堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
- GoV1.8 - 三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高