为什么要知道golang垃圾回收机制?
- 优化:工作中可能涉及到需要针对垃圾回收进行优化,所以我们要知其然,才好进行这一块的优化
- 思路:垃圾回收其实也是一种解决问题的思路,了解这种思路可以解决遇到类似的问题
- 面试:面试,这个就不多说了
golang的垃圾回收历史进程
golang v1.3 :标记清除进行垃圾回收
- 为什么选择标记清除?
- 对比:为什么没有选择引用计数,标记清除对比引用计数可以避免循环引用的问题(对比-引用计数)
- 对比:为什么没有用分代回收,因为golang 的编译器会通过逃逸分析将大部分新生对象存储栈上(栈直接被回收)
- 实现简单
- 过程
- 标记清除逻辑:
- 从程序的根对象出发,对于可达的对象进行标记
- 对未能标记的对象进行删除
- ps:标记清除原理类似数据结构中的有向图
golang v1.5 : 三色并发标记清除进行垃圾回收
1,为什么要把标记清除优化为三色并发标记?
- 因为可以降低stw的时间,提高效率
2,三色标记清除法示意图和流程描述
- 第一步:所有对象标记为白色
- 第二步:每次GC开始就从根节点开始进行遍历,把遍历到的数据放入到灰色标记表
- 第三步:继续遍历灰色节点,把灰色对象引用的白色对象放入灰色对象,之后将灰色对象放入黑色集合
- 第四步:重复第三步,直到灰色中无任何对象
- 第五步:回收白色标记表中的对象
3,怎么解决stw时间过长的问题?
- 1,stw是什么?
- 是stop the world的缩写,可以理解为触发stw后整个程序会停止运行2,如果不启动stw会怎么样?
- 在三色标记回收的过程中,一个白色对象被黑色对象引用了 且 该白色对象上游没有被任何灰色对象引用 ,但是我们不会再去遍历黑色对象表,导致该白色对象被错误的回收,造成对象丢失
- 结论:在没有stw的情况下,且满足上述标红的两个条件后会导致错误的回收,导致对象丢失
- 术语:
- 强三色不变式:不存在黑色对象引用到白色对象的指针
- 弱三色不变式:所有被黑色对象引用的白色对象都处于灰色保护状态
- ps:以上两个不变式分别对应以上标红的两个条件
- 2,如果不启动stw会怎么样?
- 在三色标记回收的过程中,一个白色对象被黑色对象引用了 且 该白色对象上游没有被任何灰色对象引用 ,但是我们不会再去遍历黑色对象表,导致该白色对象被错误的回收,造成对象丢失
- 结论:在没有stw的情况下,且满足上述的两个条件后会导致错误的回收,导致对象丢失
- 术语:
- 强三色不变式:不存在黑色对象引用到白色对象的指针
- 弱三色不变式:所有被黑色对象引用的白色对象都处于灰色保护状态
- 3,怎么解决尽量让stw时间短,又保证gc可用,不会出现对象丢失的情况?
- 解决思路:只要保证强三色不变式或者弱三色不变式中成立一个,就可以保证不会丢失对象
- 解决办法:
- 插入写屏障:如果黑色的 A 对象引用 白色的B对象,则B对象标记为灰色
- 问题:由于栈特点是容量小,速度快,所以插入屏障在栈空间中不使用,而仅仅在堆空间中使用,所以可能出现栈上发生 白色对象 被 黑色对象引用 的可能
- 解决办法:堆上三色标记清理完后,对栈启动stw(相比对堆进行stw,这里时间会很短),然后再做一遍三色标记清除
- 删除写屏障:被删除的对象如果自身为灰色或者白色,那么被标记为灰色,则这轮不会被回收,要到下轮才可能会被回收
- 4,总结插入写屏障和删除写屏障的短板
- 插入写屏障:结束后需要stw来重新扫描栈,标记栈上引用的白色对象是否真的存活
- 删除写屏障:回收精度低,会延迟一轮回收
golang v1.8 : 三色标记清除-混合写屏障
具体操作
1、GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW)
2、GC期间,任何在栈上创建的新对象,均为黑色
3、被删除的对象标记为灰色。
4、被添加的对象标记为灰色。
优点:避免了re-scan,极大的减少了stw的时间
满足条件:变形的弱三色不变式
分析
- 第一步:相当于栈上的删除写屏障:保证了不会原来被栈引用的栈对象被删除引用后,又被其他栈上对象引用,但是由于没有删除写屏障却被回收
- 第二步:相当于栈上的插入写屏障:保证了栈上新加对象不会由于没有写入屏障,从而被删除引用的时候被错误回收
- 第三步:满足删除写屏障
- 第四步:满足插入写屏障
参考链接和建议
- 参考链接
- 建议
建议可以先扫一遍博客,然后有不懂的地方可以看下b站的视频