目录
1. Go1.3之前的标记清除法(make and sweep)1. 暂停程序业务逻辑
2. 开始标记,程序找出它所有可达的对象,并做上标记(找出不可达的对象和可达对象)
3. 标记完成了之后,然后开始清除未标记的对象
4. 清除完成后,停止暂停,让程序继续跑
STW:stop the world,让程序暂停,程序出现卡顿(重要问题)
2. Go1.5三色标记法1. 起初,全部将对象标记为白色,放入白色set中(将程序作为根节点展开,见右图)
2. 遍历Root Set(深度优先遍历)得到灰色节点
2.1.先遍历第一层,将白色变成灰色
2.2.再遍历第二层,将白色变成灰色,灰色的变为黑色
2.3.在遍历第三层,将对象3由白色变为灰色
2.4.将灰色变成黑色(重复该步骤,直到灰色标记表中无任何对象,此时只有白色和黑色对象:此时,白色=垃圾,黑色=有效数据)
3.三色标记法无STW,会存在的问题
前言:三色标记法在执行标记过程中,程序不暂停(无STW),将会出现一些问题
1. 此时,三色标记法,执行的当前图见下(以“对象3”面临的场景进行分析)
2. 此时,程序没有暂停继续执行,在时间间隙内:①“对象3”可能会被“对象4”引用 ②“对象2”的P指针不在指向“对象3”
分析:此时就会出现问题,“对象2\3”标记为黑色,而“对象3”,因为“对象4”已经扫描过了,已经不会再扫描,所以“对象3”是白色,将会被清除掉 ==> 这样显然是有问题的,“对象3”被“对象4”引用,实际上“对象3”并不是垃圾,这样会导致数据丢失!
总结:三色标记法无STW,会出现有效数据被回收的场景,触发的条件为
1. 黑色对象下面引用了白色对象
2. 灰色对象与它之间的可达关系的白色对象遭到破坏
==> 为了避免上述问题的发生,最简单的方式就是引入STW机制,但是STW的过程明显存在资源浪费,会使程序发生卡顿,所以,如何能在保证对象不丢失的情况尽可能的提高GC效率,减少STW时间呢?
4. 强弱三色不变式
结论:破坏两个条件同时成立!就可以避免上面的问题,在三色标记中如果满足强/弱之一,即可保证对象不丢失
4.1.强三色不变式
破坏条件1:强制性不允许黑色对象引用白色对象
4.2.若三色不变式
破坏条件2:黑色对象可以引用白色对象,但是要求该白色对象必须存在其他灰色对象对它的引用or可达它的链路上游存在灰色对象
5. 屏障机制
采用“屏障机制”,来确保完成强/弱三色不变式,进而保证对象不会丢失
什么是屏障?可以理解为是“钩子”函数,即在执行插入/删除时,先执行“钩子”函数进行条件检查。
5.1.插入屏障/引用屏障
触发时机:对象被引用时,触发的机制(只有“堆”上的对象才触发插入屏障,“栈“上的对象不触发插入屏障,采用STW机制)
具体操作:在对象A引用对象B时,对象B被标记为灰色
满足:强三色不变式(不存在黑色对象引用白色对象的情况,因为白色对象会强制变成灰色)
示例流程:
1. 堆:对象4引用对象8,执行插入屏障(将对象8变为灰色)
2. 栈:对象1引用对象9,不执行插入屏障,执行STW暂停保护
①先把栈上的节点全部变为白色
②开启STW暂停保护
③重新扫描一次栈空间,执行标记+删除
说明:此时虽然还是有STW,但是相较之前的STW已经有了很大的提升。==> 因为当前的STW只是扫描栈空间,栈空间是有最大限制的,之前的STW栈堆空间都要扫描,所以节省了很多空间。
插入屏障的不足:在栈上的对象,存在STW,会有10~100ms的延迟
5.2.删除屏障
触发时机:对象被删除时,触发的机制
具体操作:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色
满足:弱三色不变式(保护灰色对象到白色对象的路径不会断)
示例流程:
1. 灰色对象1删除对象5,如果不触发删除屏障,那么对象5/2/3都会被删除
2. 触发删除写屏障,被删除的对象5,自身被标记为灰色
删除屏障的不足:存在延迟GC的特点(一个对象即使被删除了最后一个指向它的指针,也依旧可以活过这一轮,在下一轮GC中被删除掉)
6. 混合写屏障
为了解决下面的2个问题
1. 插入屏障的不足:结束时需要STW来重新扫描栈,大约需要10~100ms
2. 删除屏障的不足:回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉
具体操作
1. GC开始将栈上的对象全部扫描并标记为黑色(之后不在进行第2次重复扫描,无需STW)
2. GC期间,任何在栈上创建的对象,均为黑色
3. 被删除的对象标记为灰色
4. 被添加的对象标记为灰色
满足:变形的弱三色不变式(结合了插入屏障、删除屏障2者的优点)