为什么GC中有白色/灰色/黑色?

时间:2012-02-14 23:23:45

标签: c garbage-collection mark-and-sweep

我正在使用一个简单的脚本语言API实现标记和清除垃圾收集,我正在阅读垃圾收集的各种实现。像Lua这样的API使用带有白色,灰色和黑色列表的标记和扫描。

问题是,我似乎无法找到为什么有这样的清单以及为什么将它们放入这些特定颜色的解释。

在我目前的,琐碎的实现中,我只是使用“死”或“活”状态。在扫描中,删除死对象。我正在使用本机堆,因此我没有在GC中进行任何移动。

我是用C语言写的。

// Performs a full garbage collection
void GorCollect(GorContext *ctx)
{
    Value *v, *end;
    Collectable *c, *n;

    // mark stack references
    end = ctx->stack + ctx->stackTop + 1;
    v = ctx->stack;
    while(v != end)
    {
        if (gvisgc(v) && v->v.gc) // mark if a collectable obj
            Mark(v->v.gc);
        v = v++;
    }

    // mark global references
    if (ctx->global)
        Mark((Collectable *)ctx->global); // ctx->global is a collectable obj

    // perform sweep
    c = ctx->gchead; // full list of collectable objs
    ctx->gchead = 0;
    while(c) {
        n = c->next;    
        // destroy unmarked collectable
        if (!c->marked)
            FreeCollectable(ctx, c);

        // rebuild gc list (singly-linked)
        else
        {
            c->marked = 0;
            if (!ctx->gchead)
                c->next = 0;
            else
                c->next = ctx->gchead;
            ctx->gchead = c;
        }
        c = n;
    }
}

2 个答案:

答案 0 :(得分:8)

灰色表示"直播但未扫描":并非所有灰色块的后代都已标记为黑色。 增量垃圾收集器中需要灰色。它有助于标记和扫描GC继续它下次有机会进行一些标记时所做的事情。

如果您的GC是非增量式的,那么它可能会让您觉得您不一定需要灰色:您可以简单地总是递归到您遇到的任何活动块的所有孩子身上。然而,另一个更微妙的问题是这种天真的非增量递归算法可能会溢出堆栈。灰色还有助于存储有关堆中下一个访问内容的信息,而不是堆栈。

即使您为此目的使用灰色,它也不会阻止您保留要访问的块的缓冲区以提高效率。与天真递归实现的唯一区别在于,当缓冲区已满时,您将停止更新缓冲区,并且如果缓冲区在已满之后变为空,则会线性扫描堆中的灰色对象。

答案 1 :(得分:0)

首先,它们是集合,而不是列表,并且堆中的每个对象在任何时间都恰好位于其中一个集合中。

其次,在任何标记和&扫描实现它们正在被使用,但它们可能是隐含的。您没有为Mark提供实施,但在该功能中,您正在移动对象。

以下是我的垃圾收集器标记阶段的实现:

/* Initally, the white set contains all objects, the black and
   grey sets are empty. */
stack *st = m->mark_stack;
/* First all roots are added to the gray set. */
for (size_t i = 0; i < m->roots->used; i++) {
    ptr p = m->roots->array[i];
    if (p != 0) {
        /* Mark the root and move it to the gray set. */
        AT(p) |= 1;
        st_push(st, p);
    }
}
while (st->used) {
    /* Think of popping the object from the mark stack as moving
       it from the gray to the black set. */
    ptr p = st_pop(st);
    P_FOR_EACH_CHILD(p, {
        if (!GET_MARK(p_child)) {
            /* Then mark each non-black child and move it to the
               gray set. */
            AT(p_child) |= 1;
            st_push(st, p_child);
        }
    });
}
/* When control has reached this point, the gray set is empty and
   the whole heap has been divided into black (marked) and white
   (condemned) objects. */

我们可以使用三组的显式数据结构。但是对于一个世界停留的标志&amp;扫一扫gc,这个实现效率更高。