golangg gc简述
tag
垃圾回收 内存管理 三色标记法 自动释放 stw(stop the world)
go1.3之前的标记-清除(mark and sweep)方法
- 暂停程序业务逻辑,找到可达的对象和不可达的对象
- 开始标记,找出并标记程序所有可达的对象
- 标记完了后,开始清除未标记的对象
- 停止暂停,让程序继续跑。然后循环重复这个过程,知道process程序生命周期结束。
缺点:
- stw,让程序暂停,程序出现卡顿(重要问题)
- 标记需要扫描整个heap
- 清楚数据会产生heap碎片(地址不是连着的)
(后来尝试在stw后来清除gc)
go1.5 三色标记法
- 只要是新创建的对象,默认的对象都会标记为“白色”
- 每次gc回收开始,开始从根节点遍历所有对象,把遍历到的对象从白色集合放入“灰色”集合
- 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色,之后将此灰色对象放入“黑色”集合
- 重复3,直到灰色中无任何对象
- 回收所有的白色标记表的对象,也就是回收垃圾
三色标记最不希望发生的事
- 一个白色对象被黑色对象引用(白色被挂在黑色下)
- 灰色对象与他之间的可达关系的白色对象遭到破坏(灰色同时丢了该白色)
两个条件满足,就会出现对象丢失的现象。
最简单的方式就是stw
stw的过程有明显的资源浪费,对所有用户程序都有很大的影响
如何在保证对象不丢失的情况下尽可能提高gc效率,减少stw时间?
强三色不变式
- 强制性的不允许黑色对象引用白色对象
弱三色不变式
- 黑色对象可以引用白色对象,白色对象存在其他灰色对象对它的引用或者可达它的链路上游存在灰色对象。
屏障机制(强弱三色不变式实现)
1. 插入屏障(对象被引用时,触发的机制)
具体操作:在a(黑色)对象引用b(白色)对象的时候,b对象会被标记成灰色。(将b挂在a的下游,b必须被标记成灰色)
满足强三色不变式。(不存在黑色对象引用白色对象的情况了,因为白色会强制变成灰色)
//伪码
添加下游对象(当前夏有对象slot,新下游对象ptr){
// 1
标记灰色(新下游对象ptr)
// 2
当前下游对象slot = 新下游对象ptr
}
场景
A.添加下游对象(nil, B) // A之前没有下游对象,新添加一个夏有对象B,B被标记为灰色
A.添加下游对象(C,B) // A将之前下游对象C更换为B,B被标记为白色
注意:不在栈上使用插入写屏障
不足:结束时需要stw来重新扫描栈,大约需要10~100ms
2. 删除屏障(对象被删除时,触发的机制)
具体操作:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色
满足弱三色不变式。(保护灰色对象到白色对象的路径不会断)
// 伪码
添加下游对象(当前下游对象 slot, 新下游对象 ptr){
// 1
if (当前下游对象slot是灰色 || 当前下游对象slot是白色){
标记灰色(当前下游对象ptr) // slot为被删除对象,标记为灰色
}
// 2
当前下游对象slot = 新下游对象ptr
}
场景
A.添加下游对象(B, nil) // A对象删除B对象的引用。B被删除,被标记为灰(如果B之前为白)
A.添加下游对象(B,C) // A对象,更换下游B变成C。B被删除,被标记为灰(如果B之前为白)
不足:回收精度低:一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
混合屏障(go v1.8的三色标记法+混合写屏障机制)
具体操作
- gc开始将栈上的对象全部扫描并标记为黑色(之后不在进行第二次重复扫描,无需stw)。
- gc期间,任何在栈上创建的新对象,均为黑色。
- 被删除的对象被标记为灰色
- 被添加的对象标记为灰色
满足:变形的弱三色不变式(结合了插入、删除写屏障的优点)
// 伪码
添加下游对象(当前下游对象 slot, 新下游对象 ptr){
// 1
标记灰色(当前下游对象slot) // 只要当前下游对象被移走,就标记灰色
// 2
标记灰色(新下游对象ptr)
// 3
当前下游对象slot = 新下游对象ptr
}
gc三色标记并发:混合写屏障流程
栈不启用屏障,堆启用混合屏障
- gc刚刚开始,默认都为白色
- gc开始:优先扫描栈,(三色标记法)将栈对象可达节点全部标记为黑
golang还有stw只是时间很短