Sapphire 算法简要分析
序
几个月前为了分析goroutine的垃圾回收去看了Sapphire算法的论文,在博客里面也贴了第一部分的译文,
不过太监了,对此表示遗憾。于是将该篇论文重新看了一遍,然后整理了一下大致思路。个人理解,水平有限
,可能有误还望大家批评指教!
概述
支持并发的语言(多线程等)的垃圾回收工作尤为复杂,大多语言采用了以标记-回收算法为主的回收机制,
然而在并发的环境下,在进行标记回收的过程中,应用程序新生成很多对象,也会对已经存在的对象进行引用,
或是对正在被复制的对象进行更改。所以为了保持垃圾回收的正确,往往需要较为严格的同步,这种同步会
导致所谓的STW(Stop the world)。举个极端的例子,GC的时候为了保证GC正确性,所有线程都必须暂停
下来!这样的例子当然过于极端,虽然回收过程会变得相当简单,但是在实际生产的系统中,往往100ms的
暂停都是难以容忍的。Sapphire算法提供了一种思路,相比传统的做法可以大大减少STW的时间,主要原因
是去掉了读屏障,只使用了写屏障(因为应用程序写的次数要远远低于读的次数,数据库的读写分离也是靠
这点进行的优化)。
内存分区
该算法定义了内存中的3个区域,分别为U、C和S。
U区域,是指Uncollected,在一个回收周期开始之后,内存中新申请的空间。
C区域,是指一个回收周期内,内存中已有的对象,它会在回收期间分为两个子区域:O区域,指Old
,当回收器运行时就已经在内存中的对象;N区域,指New,回收器运行过程中,会复制O区域的一部分对象
到新开辟的N区域。
S区域,是指正在运行的各线程的运行栈,由于栈中内存可以自动回收,而且一般都可能会被使用。
三色标记法
标记清楚法一般是指,对堆中没有被正在运行的程序所引用(可以认为是已经没有用的)的对象和被引用的对象
标记颜色,然后删除没有引用的对象,或者将被引用的对象复制到新区域,将所有指向旧对象的指针改到新对象,
然后全部清除旧区域的对象。三色标记法是另一种标记方法(前面说的可以认为是2色标记法),包含3种颜色:
白色:白色是不可达对象(这种对象要被清除掉);黑色:黑色代表该对象自身是被强引用,而且自身的子引用
也被处理过了(这种对象就是要被留着的);灰色:灰色代表该对象自己是被强引用,但是子引用还没有被处理
(这是一个中间状态,它最终会变成黑色,但它的子引用和孙引用等不一定是黑色)。
三色标记法有一个原则,永远不许黑色的对象指向白色的对象。
标记阶段
标记阶段分为3个步骤,预标记阶段来安装标记阶段的写屏障,根标记阶段处理非栈中的所有引用树的
根然后再经过堆栈标记阶段来完成标记。
- 预标记阶段
这个阶段安装一个写屏障,类似于以下形式:
// Mark Phase Write Barrier
// this is only for pointer stores
// the update is *p = q
// the p slot may be in U or O
// the q object may be in U or O
mark-phase-write(p, q) {
*p = q;
mark-write-barrier(q);
}
mark-write-barrier(q) {
if (old(q) && !marked(q)) {
// old && !marked means "white"
enqueue-object(q);
<