Go的GC垃圾回收机制
一.历史原因
go发展史上采用过的一些方法
-
go 1.3
之前使用标记清除 -
go 1.5
三色标记法 -
go 1.8
三色标记法 + 混合写屏障机制
垃圾回收设计的名词
- 自动释放
- 垃圾回收
- 三色标记法
- 内存管理
- STW(stop the world) 全部停止执行
标记清除法
标记清除法过程
暂停程序业务逻辑
暂停程序,绘制程序和对象的可达关系。
找到所有的可达对象,并做上标记。
清除未标记对象
停止暂停
停止暂停,让程序继续跑,然后循环重复这个过程,直到程序生命周期结束。
标记清除的缺点
-
STW(stop the world):让程序暂停,程序出现卡顿(重要问题)
-
标记需要扫描整个
heap
(堆) -
清除数据会产生
heap
碎片 -
可以对以上方法进行改进(如下图),缩短
STW
的返回,但是还是会很大。
三色标记法
三色标记法的流程
三色标记法中,要切记,灰色只是一种状态,我们进行遍历最后的结果,就是没有灰色节点。
创建程序
程序创建的时候,默认所有的对象都是白色
只要是新创建的对象,默认颜色就是白色
遍历对象(非递归调用,只遍历一次),得到灰色节点
切记这里的遍历,是从根节点出发。遍历到 对象1
和 对象4
,切记 只遍历这一步,二不是递归调用。,他会把遍历到的 对象1
和 对象4
加入到灰色标记表中
遍历灰色集合
对灰色节点进行遍历,把灰色几点能达到的点,标记为灰色节点,把自己标记为黑色节点。
重复遍历灰色标记表,直到不存在灰色节点。
灰色标记表只是一种中间状态,最终我们要把灰色标记表中的东西全部清理掉。只剩下要么都是白的,要么都是黑的
回收白色垃圾
三色标记法的问题
说白了,他的问题就在于黑色节点引用白色节点了,这个白色节点,如果不停留,就会被删除掉。
三色标记法不使用 STW 会出现的问题
三色标记法最不希望发生的事
-
条件一: 一个白色对象被黑色对象引用
-
条件二:灰色对象同时又丢了对该白色对象的引用
如果以上两个条件同时满足,那么就会出现对象丢失
现象
强弱三色不变式
-
强三色不变式: 强制性的不允许黑色对象引用白色对象 :破坏了
条件一
-
弱三色不变式: 黑色对象可以引用白色对象,但是要求白色对象的链路上游,存在灰色对象 :破坏了条件二
只要满足了强三色不变式或者弱三色不变式其中任何一个条件,就可以保证对象不丢失。那么如何保证呢?就要借助于屏障机制。
屏障机制
什么是屏障
屏障就是相当于在正常的流程中,插入了一道工序,在某些个语言里,屏障也叫做 回调
,Hook
或者 handler
、钩子函数
。
插入屏障和删除屏障
屏障机制在 go
语言里,分为两种,分别为 插入屏障
和 删除屏障
插入屏障
对象被引用的时候,触发的机制
删除屏障
对象被删除的时候,触发的机制
插入屏障
具体操作: 在 A 对象引用 B 对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须标记为灰色)
这样可以满足强三色不变式,因为不存在黑色对象引用白色对象的情况了,因为白色会被强制编程灰色。
插入屏障的问题
因为每次引用对象,都触发 插入屏障
的话,会影响性能;堆栈
两种结构相比,栈对性能的要求更高,所以为了不影响 栈
上的性能,是不执行插入屏障的,只有在 堆
上才执行插入屏障。
那么栈是如何进行垃圾回收的呢?
栈
是在 堆
上的数据都扫描完了以后,然后启动 STW
,再重新扫描一遍,标记完以后,进行删除。
删除屏障
被删除的对象,如果自身是灰色或者白色,那么就会被标记为灰色
满足: 弱三色不变式。(保护灰色对象到白色对象的路径不会断)
要删除对象2
把 对象2
删除以后,要把 对象2
变成灰色
接下来,把所有的白色对象删除掉。
从上边可以看出来,这样回收删不干净,部分节点要等第二轮CG才能删除
混合屏障
单独使用删除写屏障和插入写屏障存在的问题
插入写屏障的不足:
结束时,需要STW
来重新扫描栈,大概需要 10~100ms
删除写屏障的不足:
回收精度低,一个对象即使被删除了,最后一个指向它的指针,也依旧可以活过这一轮,
三色标记 + 混合读写屏障
-
GC
开始,优先扫描栈,将栈上的可达对象全部扫描,并标记为黑色(之后不再进行第二次扫描,无需STW)。 -
GC
期间,在栈上创建的节点,也都是黑色。 -
被删除对象,标记为
灰色
-
被添加的节点,也标记为
灰色
参考文献
https://www.bilibili.com/video/BV1wz4y1y7Kd?share_source=copy_web