Go的内存回收机制(三色屏障)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go的内存回收机制(三色屏障)相关的知识,希望对你有一定的参考价值。
垃圾回收(Garbage Collection
,简称GC
)是编程语言中提供的自动的内存管理机制,自动释放不需要的内存对象,让出存储器资源。GC
过程中无需程序员手动执行。GC
机制在现代很多编程语言都支持,GC
能力的性能与优劣也是不同语言之间对比度指标之一。
Go V1.13之前的标记-清除(mark and sweep)算法
一、暂停程序业务逻辑,分类出可达和不可达对象,然后做上标记
二、清除所有未标记的对象
整个操作非常简单,但是在mark and sweep
算法执行时,程序必须暂停.即STW(stop the world)
.
STW
的过程中,CPU
不执行用户代码,全部用于垃圾回收,这个过程很耗费时间
三、停止暂停,让程序继续跑。然后循环重复这个过程,直到process
程序生命周期结束
但是缺点也很明显
- STW, 让程序暂停, 耗时卡顿(重要问题)
- 标记需要扫描整个
heap
- 清除数据会产生
heap
碎片
在Go V1.3做了一个简单优化,在开启STW的过程中只完成标记步骤,在停止STW后才开始sweep
清除
不过仍然无法避免上面的缺点.
Go V1.5的三色标记法
该算法GC
过程中可以和用户goroutine
并发运行,但需要一小段时间的STW
算法过程中三色分别是白表,灰表,黑表
Ⅰ.程序创建初,把所有对象标记未白色放入白表中
Ⅱ.GC
回收开始,从程序根节点开始遍历所有相邻对象,把遍历到的对象从白色集合中放入灰色集合
Ⅲ.遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,然后把自己放入灰色集合
Ⅳ.不断重复第三步,直到没有灰色节点,此时白表中的对象就是垃圾,可以被回收.
这么看下来好像只是一个简单的bfs
,设置的颜色似乎也很多余.
如果给这个GC
过程加上STW
,会很浪费时间,没有优化多少.
如果不加STW
,那么在回收过程中,对象间相互改变引用关系,会使垃圾回收错误.比如下面
此时[对象2]去掉对[对象3]的引用,同时[对象4]加上对[对象3]的引用
那么对象3仍然不会是垃圾,然而此时灰色节点已经到不了对象3了,对象3最后会成为垃圾被错误回收
所以在三色标记法中,会出现错误的原因有两个
- 条件1: 一个白色对象被黑色对象引用
- 条件2: 一个白色对象和灰色对象的可达关系遭到破坏
但是我们又不想使用STW
, 于是引入了一种屏障机制来破坏上述的两个条件,保证对象不丢失.
三色标记的屏障机制
强三色不变式
实质上是强制的不允许黑色对象引用白色对象, 这样就不会出现白色对象被误删的情况
插入屏障
在A对象引用B对象时,B对象被标记未灰色.
此时满足强三色不变式(不存在黑色对象引用白色对象的情况,因为白色会强制变为灰色)
添加下游对象(当前下游对象slot, 新下游对象ptr)
//1
标记灰色(新下游对象ptr)
//2
当前下游对象slot = 新下游对象ptr
插入屏障是一个很耗费性能的行为,而栈需要更高的性能要求,因此,插入屏障技术只运用在堆内存空间里,不会运用到栈里。
等全部三色标记扫描后,栈上可能仍存在白色对象被引用的情况,于是使用STW
暂停直到栈空间的三色标记结束(这次的STW
大概时间在10~100ms之间)
弱三色不变式
允许黑色对象引用白色对象时, 但需要满足条件
只有当这个白色对象存在其他灰色对象对它的引用,或它的可达链路上存在灰色对象时
删除屏障
被删除的对象,如果自身为灰色或者白色,那么就会被标记为灰色。
当然,这种方式的回收精度比较低,可能那个被删除的对象本身就是个垃圾,但是我们仍然把它在这一轮变成灰色, 无法回收。
添加下游对象(当前下游对象slot, 新下游对象ptr)
if (当前下游对象slot是灰色 || 当前下游对象slot是白色)
标记灰色(当前下游对象slot) //slot为被删除对象, 标记为灰色
当前下游对象slot = 新下游对象ptr
混合写屏障规则
①.GC
开始把栈上所有对象全部扫描并标记为黑色(之后无需二次重复扫描,无需STW)
②.GC
期间, 任何在栈上创建的新对象均为黑色
③.被删除的对象标记为灰色
④.被添加的对象标记为灰色
添加下游对象(当前下游对象slot, 新下游对象ptr)
标记灰色(当前下游对象slot) //只要当前下游对象被移走,就标记灰色
标记灰色(新下游对象ptr)
当前下游对象slot = 新下游对象ptr
这里注意,屏障技术是不在栈上应用的,因为要保证栈的运行效率
以上是关于Go的内存回收机制(三色屏障)的主要内容,如果未能解决你的问题,请参考以下文章