美文网首页
分代回收算法

分代回收算法

作者: 一蓬蒿人 | 来源:发表于2019-10-20 16:29 被阅读0次

定义

给对象增加年龄属性,针对不同年龄的对象采用不同的算法。把刚生成的对象称为新生代对象,到达一定年龄的对象则称为老年代对象。

堆结构

堆总共需要利用4个空间,分别是生成空间、2 个大小相等的幸存空间以及老年代空间,并分别用 new_start、survivor1_ start、survivor2_start、old_start 4个变量引用它们的开头。我们将生成空间和幸存空间合称为新生代空间。新生代对象会被分配到新生代空间,老年代对象则会被分配到老 年代空间里。此外我们准备出一个和堆不同的数组,称为记录集(remembered set),设为 $rs。

分代回收示意图


需要注意的是,在新生代GC时,必须考虑到从老年代空间到新生代空间的引用。


记录集

记录集被用于高效地寻找从老年代对象到新生代对象的引用。具体来说,在新生代 GC 时将记录集看成根(像根一样的东西),并进行搜索,以发现指向新生代空间的指针。

  • 记录了引用的目标对象的记录集


  • 记录了发出引用的对象的记录集


写入屏障

在分代垃圾回收中,为了将老年代对象记录到记录集里,我们利用写入屏障(write barrier)。在 应用程序更新对象间的指针的操作中,写入屏障是不可或缺的。

// $old_start 老年代起始地址
// obj 发出引用的对象
// new_obj 指针更新后成为引用目标的对象
write_barrier() {
    // 发出引用的对象是不是老年代对象
    // 指针更新后的引用的目标对象是不是新生代对象
    // 发出引用的对象是否还没有被记录到记录集中
    if(obj >= $old_start && new_obj < $old_start && obj.remembered == FALSE) {
        // $rs_index 是用于新记录对象的索引
        $rs[$rs_index] = obj;
        $rs_index++;
        obj.remembered = TRUE;
    }
    // 更新指针
    *field = new_obj;
}

对象结构

对象的头部中除了包含对象的种类和大小之外,还包含下面三种信息:
- 对象的年龄(age)
- 已经复制完毕的标志(forwarded)
- 已经向记录集记录完毕的标志(remembered)

分配

new_obj(size) {
    // 检查生成空间中是否存在 size 大小的分块
    if ($new_free + size >= $survivor1_start) {
        minor_gc();
        if ($new_free + size >= $survivor1_start) {
            allocation_fail();
        }
    }
    obj = $new_free;
    $new_free += size;
    // 对象初始化
    obj.age = 0;
    obj.forwarded = FALSE;
    obj.remembered = FALSE;
    obj.size = size;
    return obj;
}

新生代GC

copy(obj) {
    // 检测是否复制完毕
    if (obj.forwarded == FALSE) {
        // 检测对象的年龄
        if (obj.age < AGE_MAX) {
            // 复制到幸存空间
            copy_data($to_survivor_free, obj, obj.size);
            obj.forwarded = TRUE;
            obj.forwarding = $to_survivor_free;
            $to_survivor_free.age++;
            $to_survivor_free += obj.size;
            // 复制子对象
            for (child : children(obj)) {
                *child = copy(*child);
            }
        } else {
            // 晋升到老年代
            promote(obj);
        }
    }
    return obj.forwarding;
}

promote(obj) {
    // 老年代分配空间
    new_obj = allocate_in_old(obj);
    // 若失败,则进行老年代GC
    if (new_obj == NULL) {
        major_gc();
        new_obj = allocate_in_old(obj);
        if (new_obj == NULL) {
            allocation_fail();
        }
    }
    obj.forwarding = new_obj;
    obj.forwarded = TRUE;
    for (child : children(new_obj)) {
        // 检测obj 是否有指向新生代对象的指针
        if (*child < $old_start) {
            // 将new_obj添加到记录集中
            $rs[$rs_index] = new_obj;
            $rs_index++;
            new_obj.remembered = TRUE;
            return;
        }
    }
}

minor_gc() {
    // 分配To幸存空间的分块
    $to_survivor_free = $to_survivor_start;
    // 复制能从根找到的新生代对象
    for (r : $roots) {
        if (*r < $old_start) {
            *r = copy(*r);
        }
    }
    // 搜索记录集中记录的对象 $rs[i],执行子对象的复制操作
    i=0
    while (i < $rs_index) {
        has_new_obj = FALSE
        for (child : children($rs[i])) {
            if (*child < $old_start) {
                *child = copy(*child);
                // 检测对象保留在新生代,还是晋升到老年代
                if (*child < $old_start) {
                    has_new_obj = TRUE;
                }
            }
        }
        // 若为FALSE,则老年代对象 $rs[i] 就已经没有指向新生代空间的引用了
        if (has_new_obj == FALSE) {
            // 将这个元素从记录集里删除
            $rs[i].remembered = FALSE;
            $rs_index--;
            swap($rs[i], $rs[$rs_index]);
        } else {
            i++;
        }
    }
    swap($from_survivor_start, $to_survivor_start);
}

相关文章

网友评论

      本文标题:分代回收算法

      本文链接:https://www.haomeiwen.com/subject/fuhamctx.html