1. Go V1.3之前的标记清除(mark and sweep)
1. 1 标记清除法的基本流程
第一步 : STW(stop the world)暂停所有的程序业务逻辑, 找出可达对象和不可达对象(程序调用对象)
第二步 : 开始标记, 程序找出他所有的可达对象并进行标记
第三步 : 清除所有不可达对象, 就是未标记的对象
第四步 : 停止暂停, 让程序继续跑, 然后重复这个过程, 直到程序结束
1. 2 标记清除的缺点
- STW, 会让程序变慢
- 标机过程需要将全部的堆栈进行一遍扫描来确定是不是可达对象
- 删除的过程中, 会产生heap碎片
2. Go V 1.5 三色标记法
2. 1三色标记法的基本流程
三种颜色 白色, 灰色, 黑色. 总图在这
- 第一步首先将所有对象加入白色标记表中
- 第二步 : 将程序不依靠指针能够找到的所有对象标为灰色, 并在白色表中将这些节点删除
- 第三步 : 遍历灰色表中的节点, 将表中灰色节点能找到的白色节点标为灰色, 并且将原先的灰色节点标为黑色
- 第四步 : 重复进行第三步的步骤直至所有的灰色节点都变为了黑色节点, 那么白色的节点就是要删除的节点
2. 2 如果没有STW保护的三色标记法会出什么问题呢 ?
假如没有STW的保护, 那么我们在标记和程序运行的过程中, 其实这两个过程是并行发生的.
那么会不会发生如下情况
那么当我们遍历到对象2时, 对象3就无法被标记为灰色对象了. 因为我们是不会再去遍历黑色的对象. 那么此时对象3就会被误当为垃圾对象给处理了
所以这是golang中三色标记法最不愿意看到的一幕
如果上述两个条件都满足了, 那么很有可能发生对象丢失!!!
那么该如何解决呢 ?
其实最简单的方法就是加STW
但是STW对于程序有很大的资源浪费.
那么如何保证有效执行GC期间又减少STW的时间来加快效率呢
我们首先介绍一下强弱三色不变式, 再来介绍如何解决这个问题
2. 3 强弱三色不变式
只要解决上述两个问题, 那么就可以保证对象不被丢失.
所以Google提出了两种方法
只要让上面两个条件不满足一个即可
- 强三色不变式
一个白色的条件不允许挂在黑色条件下 - 弱三色不变式
黑色条件下可以挂白色条件但是白色条件要能被找到 (意思就是白色条件要被灰色条件所指向, 或者是在白色条件的链路上游能找到灰色条件)
3. 屏障法(用来满足强弱三色不变式)
3. 1 插入屏障
具体操作
: 在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色)
满足: 强三色不变式
. (不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)
我们知道,黑色对象的内存槽有两种位置, 栈
和堆
.
栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用, 所以“插入屏障”机制,在栈空间的对象操作中不使用
. 而仅仅使用在堆空间对象的操作中使用
.
接下来,我们用几张图,来模拟整个一个详细的过程, 希望您能够更可观的看清晰整体流程。
插入屏障的缺点
还是需要STW的加入, 对程序资源消耗大, 但是相对于之前的全局STW, 那又小了很多
3. 2 删除屏障
具体操作
: 开始时, 利用快照的方式, 需要进行STW, 然后被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。
满足: 弱三色不变式
. (保护灰色对象到白色对象的路径不会断)
删除屏障的缺点
如上图所示, 对象1删除了对于对象5的引用, 所以对象5被标记为了灰色, 最后的又被标记为了黑色, 那么他不是没有被删除了吗 ?
实际上这就是删除屏障的缺点 :
这种方式的回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
但是这个对象就是会在下一轮GC中被删除
4. Go V1.8 混合写屏障
具体操作
:
1、GC开始将栈上的可达对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
2、GC期间,任何在栈上创建的新对象,均为黑色。
3、被删除的对象标记为灰色。
4、被添加的对象标记为灰色。
满足: 变形的弱三色不变式.
这里我们注意, 屏障技术是不在栈上应用的,因为要保证栈的运行效率。
混合写屏障是GC的一种屏障机制,所以只是当程序执行GC的时候,才会触发这种机制。
Golang中的混合写屏障满足弱三色不变式
,结合了删除写屏障和插入写屏障的优点.
只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW.
而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行re-scan操作了,减少了STW的时间。
5. 总结
GoV1.3- 普通标记清除法,整体过程需要启动STW,效率极低。
GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高。
6. 现代GC流程
标记之前和标记完成之后需要进行短暂的STW. 来开启和终止写屏障