golang 垃圾回收
GO1.3之前的版本 标记清除法
标记清除:GC流程
- 暂停程序业务逻辑,找出当前没有直接应用的对象,和直接引用的对象
- 标记直接引用对象
- 删除未被标记的对象
- 停止暂停,恢复程序运行
- 循环往复,知道程序停止
- 标记
- 删除未标记对象
标记清除:缺点 STW
- 产生程序卡顿-STW(程序中断垃圾回收)(解决:先恢复运行,后台清除未标记对象,缩短暂停时间)
- 会产生很多堆的碎片
GO1.5的版本 三色标记法
三色标记:GC流程
-
创建对象就标记为白色
-
GC回收开始从
ROOT SET
根节点遍历,把遍历到的对象,从白色集合放到黑色集合中
-
遍历灰色集合,将灰色集合引用的对象,从白色集合放入黑色集合中。之后将灰色对象放入黑色集合中。
-
重复第3步骤,直到灰色中没有任何对象。(只有黑色和白色)
-
回收所有白色对象,GC
三色标记:缺点以及优化
产生问题
- 白色对象被当前的黑色对象引用
- 之前的灰色对象与白色对象之间引用关系结束
如上两个条件同时满足,则白色对象被误删
优化:强弱三色不变式
强弱三色不变式
三色标记只要满足 强/弱三色不变式之一,就可以保证对象不丢失
强三色不变式
弱三色不变式
黑色对象可以引用白色对象,但是白色必须被灰色对象引用,黑色才可以指向白色
屏障机制
插入屏障
只在堆函数上作用,因为堆中存储的内存占比较大,栈中存储的及时运行程序的内存(方法压栈),保证程序稳定运行,所以只作用的堆上。
三色标记扫描完成后,会STW栈空间,重新扫描(因为栈没有触发写屏障),后完成垃圾回收。
- 插入规则:
对象被引用触发
。当插入新节点挂载载上一节点下,新节点被标记为灰色。(伪代码如下) - 优势:满足强弱三色不变式(不存在黑色引用白色对象,白色都强制变为灰色)
- 缺点:需要STW,重新扫描堆空间(栈不触发插入写屏障)
func 添加下游对象(当前下游对象ptr, 新的下游对象) {
// 1
标记为灰色(新的下游对象)
// 2
当前下游对象ptr = 新的下游对象
}
删除屏障
- 删除规则:被删除对象,自身是黑色或者白色,则被标记为灰色
- 满足:弱三项不定式(保证后续对象引用不被删除)
func 删除下游对象(当前下游对象, 新下游对象) {
if 当前下游对象==灰色 || 当前下游对象==白色 {
标记为灰色(当前下游对象)
}
当前下游对象 = 新下游对象
}
// 引用链 A->B->C->D
// 直接删除B,则B变为灰色对象
删除下游对象(A, nil)
// 则A对象旧指针指向的对象变为灰对象,B对象边灰对象
删除下游对象(A, C)