Goland GC
- 引用
- [Go 1.3 mark and sweep 标记法](#Go 1.3 mark and sweep 标记法)
- [Go 1.5 三色标记法](#Go 1.5 三色标记法)
- [Go 1.8 混合写屏障(hybrid write barrier)机制](#Go 1.8 混合写屏障(hybrid write barrier)机制)
- 总结
引用
https://zhuanlan.zhihu.com/p/675127867
- Garbage Collection,缩写为GC,一种内存管理回收的机制
Go 1.3 mark and sweep 标记法
流程:
- 暂停程序
- 从入口标记所有可达的对象
- 删除不可达的对象
- 恢复程序
- 这种方式存在一个STW(stop the world)时间。
Go 1.5 三色标记法
- 解决上个版本存在STW的问题。
- 三个颜色:
白色:尚未访问过
黑色对象已访问过,而且本对象 引用到 的其他对象 也全部访问过了
灰色对象已访问过,但是本对象 引用到 的其他对象 尚未全部访问完。全部访问后,会转换为黑色。
流程:
- 每次新创建的对象,默认的颜色都是标记为"白色"
上图所示,我们的程序可抵达的内存对象关系如左图所示,右边的标记表,是用来记录目前每个对象的标记颜色分类。这里面需要注意的是,所谓"程序",则是一些对象的根节点集合。所以我们如果将"程序"展开,会得到类似如下的表现形式,如图所示。
Root Set根节点集合:程序运行到当前时刻的栈和全局数据区域。
BFS遍历一层,使用两个数组:灰色数组和黑色数组。
- 把白色可达的1,4加入灰色。
- 灰色遍历后变成黑色,即 1,4变成黑色。
- 灰色遍历到的2,7由白转灰。
- 反复遍历灰色直至没有灰色。
- 回收白色。
但如果GC过程中程序在运行,上述引用关系会实时改变,则此方法会误删白色对象。
在运行时能导致错误GC的场景是:
- 白色被黑色所引用
- 灰色到白色的引用失效
屏障机制
谷歌团队引入了强弱三色不变式 :
强制 不允许黑色引用白色
弱:所有被黑色对象引用的白色对象都处于灰色保护状态
弱三色不变式强调,黑色对象可以引用白色对象,但是这个白色对象必须存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象。 这样实则是黑色对象引用白色对象,白色对象处于一个危险被删除的状态,但是上游灰色对象的引用,可以保护该白色对象,使其安全。
为了遵循上述的两个方式,GC算法使用两种屏障方式:插入屏障 和删除屏障。
插入屏障
具体操作: 改色:A引用B时,B改为灰色。(重新参与到bfs里)
满足: 强三色不变式. (不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)
黑色对象的内存槽有两种位置, 栈和堆. 栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用, 所以"插入屏障"机制,在栈空间的对象操作中不使用. 而仅仅使用在堆空间对象的操作中.
这样可以避免堆中操作出现白色对象误删的情况。至于栈中操作,开启STW确保安全性。
这样此方案可以解决在堆操作不需要开启STW。
删除写屏障
删除写屏障是指:被删除引用关系的对象,如果自身为灰色或者白色,那么被标记为灰色。这一点其实就是为了满足弱三色不变式。
满足: 弱三色不变式. (保护灰色对象到白色对象的路径不会断)
比如A 此时引用了B对象,但是此时A要删除和对象B的引用关系此时。此时如果B对象是白色对象那么B对象会被置为灰色。还有一种情况就是
A对象之前引用了B对象此时,此时A更换了引用关系引用C也就是A对象和B对象之间的引用关系被删除掉了。此时B对象会被置为灰色。
此时对象1想要删除和对象5之间的引用关系,此时就会触发删除写屏障。
- 正常删除1到5的引用后,GC应该把 2 3 5都回收了。但是因为保护机制,保留了235,在下一轮GC再处理。
总结
至此可以解决堆上新增引用的STW的问题,还有堆和栈上删除引用不需要STW,只需要下一次GC来回收。
还遗留了栈上新建引用导致的STW问题。
Go 1.8 混合写屏障(hybrid write barrier)机制
- 混合写屏障一开始将栈上对象全部标记为黑色
- 在GC期间任何在栈上创建的对象均为黑色。
- 被删除引用的对象均标记为灰色
- 被添加引用的对象均标记为灰色
- 栈不开启屏障机制
也就是BFS对栈上对象的初始化总为黑色,运行时的引用关系变化后的子节点总是标记为灰色,参与到下一轮的BFS里。
主要为了满足弱三色不变式。注意混合写屏障是Gc的一种屏障机制,所以只是当程序执行GC的时候,才会触发这种机制。
GC刚开始所有对象都是白色的,然后第一步我们扫描栈区将栈上的对象全部标记为黑色
运行时 1 新建了到7的引用。
7会被置灰,后续运行时4删除7的引用直接删即可。
在栈上新增 9,按照规则在栈上创建的对象均为黑色对象。
新增9到3的引用 直接添加即可。
2删除3的引用关系,在栈上删除即可。不存在误删
新建引用关系10-7,会让7置灰,避免7被删除
4引用2,2本来就是黑色不影响。
对象1删除和对象2之间的引用关系直接删了就可以,对象4删除和对象7之间的引用关系此时由于是在堆上所以了触发了这个混合写屏障机制将对象7置为灰色。
下一轮GC会处理 7 6 11.
总结
Go 1.3版本:普通标记清除法,整体过程需要启动STW,效率极低。
Go 1.5版本: 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
Go 1.8版本:三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高。