图解Go1.3标记清除和1.5三色标记

0 阅读6分钟

Go V1.3标记清除算法:

普通标记清除算法.这个算法有两个步骤.

1).标记(Mark Phase).

2).清除(Sweep Phase).

过程清晰明了.实则是找到需要清除的内存数据.然后一次性清除.

标记清除详细过程:

1).暂停业务逻辑.对可达和不可达的对象进行分类.然后做上标记.如图所示.

图中表示的是程序与对象的可达关系.目前程序的可达对象有对象1->2->3.对象4->7等5个对象.

2).开始标记.程序找出它所有可达的对象.并做上标记.如图所示.

对象1->2->3 对象4->7等5个对象被做上标记.

3).标记完了以后.开始清除未标记的对象.如图所示.

操作非常简单.但是有一点需要注意.Mark and Sweep算法在执行的时候,需要程序暂停,即STW(Stop The World).在STW过程中.CPU不执行用户代码.全部用于垃圾回收.这个过程影响很大.所以STW也是垃圾回收最大的难题和优化的点.所以在执行第三步这段时间.程序会暂停和停止任何工作,卡住等待回收执行完毕.

4).停止暂停.让程序继续运行.然后重复这个过程.直到Process程序生命周期结束.

标记清除缺点:

1).STW让程序暂停.所以程序会出现卡顿.

2).标记需要扫描整个Heap.

3).清除数据会产生Heap碎片.

执行GC的基本流程就是首先启动STW.使程序暂停.然后执行标记.在执行数据回收.最后停止STW.如果所示.

从上图可以看到.全部的GC时间都是包裹在STW范围之内的.这样程序的暂停时间貌似很长.影响性能.所以进行了优化.将STW步骤提前.缩小暂停时间范围.如图所示.

从上图可以看到主要是将STW的步骤提前了一步.因为在Sweep清除的时候.可以不需要STW停止.这些对象已经不是可达对象了.不会出现回收写冲突等问题.

Go V1.5三色标记法:

三色标记过程:

1).每次新创建的对象.默认的颜色都被标记为"白色".如图所示.

左边为程序可抵达的内存对象关系.右边的标记表用记录目前每个对象的标记颜色分类.注意.所谓"程序".则是一些对象的根节点集合.如果将程序展开.会得到如下图表现形式.

2).每次执行GC回收.都会从根节点开始遍历所有对象.把遍历到的对象从白色集合放入"灰色"集合.如图所示.

注意.本次遍历是一次遍历.非递归形式.是从程序抽出可抵达的对象遍历一层.如上图所示.当前可抵达对象是对象1和对象4.表示本轮遍历结束.对象1和对象4会被标记为灰色.灰色标记表就会多出这两个对象.

3).遍历灰色对象集合.将灰色对象引用的对象从白色集合放入灰色集合.之后将此灰色对象放入黑色集合.如图所示.

第一次遍历只扫描灰色对象.将灰色对象的第一层遍历可抵达对象由白色变为灰色.如对象2 对象7.而之前的灰色对象1和对象4则会被标记为黑色.同时由灰色标记移动到黑色标记.

4).重复第三步.直到灰色对象中无任何对象.如下图所示.

当全部的可达对象都遍历完后.灰色标记表将不再存在灰色对象.目前全部内存的数据只有两种颜色.即黑色和白色.黑色对象就是程序逻辑可达(需要)的对象.这些数据目前支持程序正常业务的运行.是合法的有用的数据.不可删除.白色对象是目前全部不可达的对象.程序不依赖它们.所以白色对象是内存中目前的垃圾数据.需要被清除.

5).回收所有的白色标记的对象.也就是回收垃圾.如图所示.

将全部的白色对象进行删除回收.剩下的就是全部依赖的黑色对象.

以上就是三色并发标记法.上述流程清楚的体现了三色的特性.但是这里可能有很多并发流程也会被扫描.执行并发流程的内存可能相互依赖.为了GC过程中保证数据安全.在开始三色标记之前会加上STW.在扫描确定黑白对象之后在放开STW.很明显这样的GC扫描的性能很低.

没有STW的三色标记:

把初始状态设置为经历了第一轮扫描.目前黑色对象有1和对象4.灰色的对象有对象2和对象7.其他的对象为白色对象.并且对象2是通过指针p指向对象3的.如图所示.

现在如果在三色标记过程中不启动STW.则在GC扫描过程中.任意对象均可能发生读写操作.如图所示.

在还没有扫描到对象2的时候.已经将对象4标记为黑色.此时创建q指针.并且指向白色对象3.

与此同时灰色的对象2将指针p移除,此时白色的对象3被挂在已经扫描完成的黑色对象4下.如图所示.

然后按照正常的的算法逻辑.将所有灰色对象标记为黑色.这样对象2和对象7就被标记成了黑色.如图所示.

接下来就是最后一步.将所有白色对象当做垃圾进行回收.如图所示.

最后得到的结果是.本来对象4是合法引用的对象3.最后被GC给"误杀"回收掉了.

触发三色标记不安全的必要条件:

有两种情况在三色标记法是不希望发生的.

1).一个白色对象被黑色对象引用.(白色被挂在黑色下).

2).灰色对象与它之间的可达关系的白色对象遭到破坏.(灰色同时丢了该白色.)

如果上面两个条件同时满足.就会出现丢失对象的现象.

为了防止这种现象.最简单的方式就是STW.直接禁止其他用户程序对对象引用关系的干扰.但是STW的过程明显资源浪费.对所有用户程序都有很大影响.