GC原理
垃圾回收,是一种自动内存管理机制
垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的内存对象,让出存储器资源。GC过程中无需程序员手动执行。GC机制在现代很多编程语言都支持,GC能力的性能与优劣也是不同语言之间对比度指标之一。
Go 1.3之前的标记-清除算法
- 标记
- 清除
具体步骤
- 暂停程序业务逻辑, 分类出可达和不可达的对象,然后做上标记。
- 开始标记,程序找出它所有可达的对象,并做上标记。
- 标记完了之后,然后开始清除未标记的对象.
- 停止暂停,让程序继续跑。然后循环重复这个过程,直到process程序生命周期结束。
mark and sweep算法在执行的时候,需要程序暂停!即 STW(stop the world)
,STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,所以STW也是一些回收机制最大的难题和希望优化的点。
缺点
- STW,stop the world;让程序暂停,程序出现卡顿 (重要问题);
- 标记需要扫描整个heap;
- 清除数据会产生heap碎片。
Go 1.5的三色并发标记法
流程:
- 每次新创建的对象,默认的颜色都是标记为"白色"
- 每次GC回收开始, 会从根节点开始遍历所有对象,把遍历到的对象从白色集合放入"灰色"集合
- 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
- 重复第三步, 直到灰色中无任何对象
- 回收所有的白色标记表的对象. 也就是回收垃圾
如果没有STW的三色标记法会出现的问题
有两种情况,在三色标记法中,是不希望被发生的。
- 条件1: 一个白色对象被黑色对象引用**(白色被挂在黑色下)**
- 条件2: 灰色对象与它之间的可达关系的白色对象遭到破坏**(灰色同时丢了该白色)**
如果当以上两个条件同时满足时,就会出现对象丢失现象!
为了防止这种现象的发生,最简单的方式就是STW,直接禁止掉其他用户程序对对象引用关系的干扰,但是STW的过程有明显的资源浪费,对所有的用户程序都有很大影响。那么是否可以在保证对象不丢失的情况下合理的尽可能的提高GC效率,减少STW时间呢?答案是可以的,我们只要使用一种机制,尝试去破坏上面的两个必要条件就可以了。
屏障机制
强-弱三色不变式
- 强三色不变式;不存在黑色对象引用到白色对象的指针。强制性的不允许黑色对象引用白色对象,这样就不会出现有白色对象被误删的情况。
- 弱三色不变式;所有被黑色对象引用的白色对象都处于灰色保护状态。黑色对象可以引用白色对象,但是这个白色对象必须存在其他灰色对象对它的引用,或者可达它的链路上游存在灰色对象。 这样实则是黑色对象引用白色对象,白色对象处于一个危险被删除的状态,但是上游灰色对象的引用,可以保护该白色对象,使其安全。
插入屏障
触发时机:对象被引用时。
具体操作:在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色)
强三色不变式. (不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)
不足:结束时需要STW来重新扫描栈,大约需要10~100ms
删除屏障
具体操作
: 被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。
满足
: 弱三色不变式. (保护灰色对象到白色对象的路径不会断)
不足:回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
Go 1.8混合写屏障机制
具体操作
:
1、GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
2、GC期间,任何在栈上创建的新对象,均为黑色。
3、被删除的对象标记为灰色。
4、被添加的对象标记为灰色。
满足
: 变形的弱三色不变式.
场景一: 对象被一个堆对象删除引用,成为栈对象的下游
场景二: 对象被一个栈对象删除引用,成为另一个栈对象的下游
场景三:对象被一个堆对象删除引用,成为另一个堆对象的下游
场景四:对象从一个栈对象删除引用,成为另一个堆对象的下游
Golang中的混合写屏障满足弱三色不变式
,结合了删除写屏障和插入写屏障的优点,只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW,而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行re-scan操作了,减少了STW的时间。
总结
GoV1.3- 普通标记清除法,整体过程需要启动STW,效率极低。
GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高。