目录
介绍GC
概要
什么是根对象
三色标记法
什么情况下三色标记法会失效
屏障机制
“强-弱” 三色不变式
插入屏障 (强三色)
删除屏障(弱三色)
Go 的混合写屏障机制
混合写屏障规则
介绍GC
概要
- 作用范围:只回收堆内存,不回收栈内存(函数执行完毕后直接释放)
- 主流 GC 算法:引用计数(Python)、分代收集(Java)、标记-清除(Golang 使用三色标记法)
- 触发时机:
- 主动触发:调用 runtime.GC()
- 被动触发:定时触发(默认为 2 分钟)、当前堆内存占用为上次 GC 后内存占用的两倍
什么是根对象
全局变量:编译器就能确定存在于程序整个生命周期的变量
执行栈:每个 goroutine 都有自己的执行栈,执行栈上包含栈上的变量及指向分配的堆内存指针
寄存器: 寄存器的值可能表示一个指针,参与计算的这些指针可能指向某些赋值器分配的堆内存块
三色标记法
Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一定时间的STW(stop the world),
所谓三色标记法实际上就是通过三个阶段的标记来确定清楚的对象都有哪些?
我们来看一下具体的过程。
第一步 , 每次新创建的对象,默认的颜色都是标记为“白色”,如图所示。
第二步, 每次GC回收开始, 会从根节点开始遍历所有对象,把遍历到的对象从白色集合放入“灰色”集合如图所示。
如果 本次到 对象1 和 对象4 便遍历完,本次GC结束
第三步, 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合,如图所示。
第四步,重复上面的步骤我们可以得到如下
剩下的就是全部依赖的黑色对象,然后我们把白色的对象删除,本轮GC 结束
什么情况下三色标记法会失效
以下条件在三色标记法中,是不希望被发生的。
条件1: 一个白色对象被黑色对象引用(白色被挂在黑色下)
条件2: 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色同时丢了该白色)
如果当以上两个条件同时满足时,就会出现对象丢失现象!
如图所示,对象2原本指向对象3,但是丢了对象3 黑色对象4 已经完成扫描了,但是对象3挂在上面
这就导致 对象三 没有被扫描 变成灰色 进入 灰色表内,导致被GC给“误杀”回收掉了。
为了防止这种现象的发生,最简单的方式就是STW,直接禁止掉其他用户程序对对象引用关系的干扰,但是STW的过程有明显的资源浪费,对所有的用户程序都有很大影响。
那么是否可以在保证对象不丢失的情况下合理的尽可能的提高GC效率,减少STW时间呢?
答案是可以的,我们只要使用一种机制,尝试去破坏上面的两个必要条件就可以了。
屏障机制
“强-弱” 三色不变式
强三色不变式
不存在黑色对象引用到白色对象的指针。
强三色不变色实际上是强制性的不允许黑色对象引用白色对象,就不会出现有白色对象被误删况。
弱三色不变式
弱三色不变式强调,黑色对象可以引用白色对象,但是这个白色对象必须存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象。 这样实则是黑色对象引用白色对象,白色对象处于一个危险被删除的状态,但是上游灰色对象的引用,可以保护该白色对象,使其安全。
插入屏障 (强三色)
具体操作:在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色)
满足:强三色不变式. (不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)
流程:
对于 堆对象来说 新挂上的对象标记为灰色,如图所示 对象3原本为白,被标记为灰
对 栈对象来说: 如图 新加入的 对象8 为白色 挂在黑色对象1上
如果栈不添加,当全部三色标记扫描之后,栈上有可能依然存在白色对象被引用的情况(如上图的对象8). 所以要对栈重新进行三色标记扫描, 但这次为了对象不丢失, 要对本次标记扫描启动STW暂停. 直到栈空间的三色标记结束.
为什么对栈对象不进行 插入写屏障?
因为栈空间调用更加频繁,且更加快速,这里如果每次插入的时候都检查(父节点是否为黑),会让栈的速度变慢,而栈空间和堆空间来比较的话,就是快,所以这里为了性能,不进行插入屏障,而是在最后再重新扫描一次
删除屏障(弱三色)
具体操作: 被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。
满足: 弱三色不变式. (保护灰色对象到白色对象的路径不会断)
会导致什么呢? 本应该被清理的 对象2 偷偷活过了这一轮 GC 检查
这种方式的回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
Go 的混合写屏障机制
插入写屏障和删除写屏障的短板:
插入写屏障:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;
删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
Go V1.8版本引入了混合写屏障机制,避免了对栈重新检查的过程,极大的减少了STW的时间。结合了两者的优点。
混合写屏障规则
具体规则
1、GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
2、GC期间,任何在栈上创建的新对象,均为黑色。
3、被删除的对象标记为灰色。
4、被添加的对象标记为灰色。
满足
变形的弱三色不变式.
拿 插入写屏障 的图作对比
新加入的栈节点也为黑
拿 删除写屏障 的图作对比
避免 某些对象多存活一轮
总结:栈上的对象(包括其引用对象)赦免,堆上的对象既进行插入屏障,又进行删除屏障