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

发布于 2025-01-05 08:42:46 字数 1322 浏览 1 评论 0原文

我正在使用一个简单的脚本语言 API 来实现标记和清除垃圾收集,我正在研究该 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;
    }
}

I'm implementing mark-and-sweep garbage collection in a simple scripting language API I'm working on and have been reading about various implementations of garbage collection. API's such as Lua use mark-and-sweep with white, gray and black lists.

The problem is, I can't seem to find explanation of why there are such lists and why they are put into these specific colours.

In my current, trivial implementation, I simply use "dead" or "alive" states. In the sweep, the dead objects are deleted. I'm using the native heap so I'm not doing any moving in my GC.

I'm writing it in 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;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

请持续率性 2025-01-12 08:42:46

灰色意味着“实时但未扫描”:尚未将灰色块的所有后代标记为黑色。灰色在增量垃圾收集器中是必需的。它可以帮助标记和清除 GC 在下次有机会做一些标记时继续它正在做的事情。

如果您的 GC 是非增量的,那么您可能认为您不一定需要灰色:您可以简单地始终递归遇到的任何活动块的所有子级。然而,另一个更微妙的问题是,这种幼稚的非增量递归算法可能会溢出堆栈。灰色也有帮助,因为它允许在堆(而不是堆栈)中存储有关下一步要访问的内容的信息。

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

Gray means "live but not scanned": not all the descendants of a gray block have been marked as black yet. The gray color is necessary in an incremental garbage-collector. It helps a mark-and-sweep GC continue what it was doing the next time it gets a chance to do a bit of marking.

If your GC is non-incremental, then it may look to you like you don't necessarily need the gray color: you can simply always recurse on all the children of any live block you encounter. However, another more subtle issue is that this naive non-incremental recursive algorithm may overflow the stack. A gray color also helps by allowing to store information about what to visit next in the heap, instead of the stack.

Even if you use the gray color for this purpose, it doesn't prevent you from keeping a buffer of blocks that remain to be visited for efficiency. The only difference with the naive recursive implementation is that you stop updating the buffer when it's already full, and you scan the heap linearly for gray objects if the buffer becomes empty after having been full.

水溶 2025-01-12 08:42:46

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

其次,在任何标记和正在使用它们的扫描实现,但它们可能是隐含的。您没有提供 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. */

我们可以对这三个集合使用显式数据结构。但对于一个停止世界的标记&扫GC,这样的实现效率要高很多。

First of, they are sets, not lists and each object in the heap is at any time in exactly one of the sets.

Second, in any mark & sweep implementation they are being used, but they may be implied. You don't provide the implementation for Mark, but in that function you are moving your objects in your sets.

Here is the implementation for the mark phase of my garbage collector:

/* 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. */

We could instead use explicit data structures for the three sets. But for an stop-the-world mark & sweep gc, this implementation is much more efficient.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文