Golang Garbage Collector
Go 1.3 mark and sweep方法
步骤:
- 第一步暂停程序业务逻辑,找出所有对象,找出不可达对象,和可达对象
- 第二步开始标记,程序找出它所有可达的对象,并做上标记
- 标记完成,清除未标记的对象
- 停止STW
业界常见的垃圾回收算法有以下几种:
- 引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0时回收该对象。
- 优点:对象可以很快地被回收,不会出现内存耗尽或达到某个阀值时才回收。
- 缺点:不能很好地处理循环引用,而且实时维护引用计数,也有一定的代价。
- 代表语言:Python、PHP、Swift
- 标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记为”被引用”,没有被标记的进行回收。
- 优点:解决了引用计数的缺点。
- 缺点:需要STW,即要暂时停掉程序运行。标记需要扫描整个heap。清除数据会产生heap碎片
- 代表语言:Golang(其采用三色标记法)
- 分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,而短的放入新生代,不同代有不同的回收算法和回收频率。
- 优点:回收性能好
- 缺点:算法复杂
- 代表语言: JAVA
Go1.5 三色标记法
维护三个集合。
白色:所有对象起初全标记为白色,表明还没有遍历到的。
GC开始需要做初始化灰色结点,即需要从定义的ROOT集合遍历找到对应的白色结点,将对应的白色结点转为灰色结点。
灰色:遍历第一次得到灰色对象。
黑色:遍历灰色结点,灰色结点如果仍能找到对象可达则标记为黑色,而下一个节点则标记为灰色。
重复第一步,直到灰色表中没有任何对象。
收集白色对象(垃圾, 没有再被任何使用到)
三色标记无STW的问题
三色标记STW性能比较低。
存在的问题:灰色对象2引用的对象3,可能被用户程序修改,而同时被标记位黑色的对象4又引用了该对象3,因为黑色对象已经不会被扫描,灰色对象2已经不再引用对象3,对象不能被标记为灰色,故最后被GC掉,但是对象4存在引用关系,就会发生错误。
强弱三色不变式
强三色不变式 -- 破坏条件1
强制性的不允许黑色对象引用白色对象。
黑色可以引用灰色对象,但是不允许引用白色对象。
弱三色不变式 -- 破坏条件2
黑色可以引用白色对象,白色对象存在其他灰色对象对它的引用(存在另外一条路可达)。
满足强弱之一,即可保证对象不丢失
插入写屏障 -- 对象被引用的时候 触发的机制
A对象引用B对象,B对象被标记为灰色对象。(如果现在是黑色引用白色 就会强制把白色变为黑色 强三色不变式)
栈不启用插入屏障,堆启用插入屏障
在准备回收白色之前,需要重新扫描一遍栈空间,加入STW暂停保护栈,防止外界干扰
这样相当于2遍扫描栈 如果没有第一次 会发生什么?
删除屏障 -- 对象被删除的时候 触发的机制
被删除的对象,如果自身为灰色或者白色,那么被标记为灰色(弱三色不变式 保护灰色对象到白色对象的路径不会断)
回收精度比较低,每次都可以活过一轮,在下一轮GC中被回收
Go 1.8 三色标记 + 混合写
具体操作:
- GC开始将栈上的对象全部扫描并标记为黑色(不需要重复扫描以及STW)
- GC期间,任何在栈上创建的对象,均为黑色
- 被删除的对象标记为灰色
- 被添加的对象标记为灰色
满足变形的弱三色不变式(结合插入、删除写屏障两者的优点)
1 满足弱三色
2 满足强三色
混合写屏障场景1:对象被一个堆对象删除引用,成为栈对象的下游
混合场景二:对象被一个栈对象删除引用,成为另外一个栈对象的下游
混合场景三:对象被一个堆对象删除引用,成为另外一个堆对象的下游
对象10引用对象7的时候就会将对象7标记为灰色