【问题标题】:Why white/gray/black in GC?为什么在 GC 中使用白色/灰色/黑色?
【发布时间】:2012-02-14 23:23:44
【问题描述】:

我正在使用我正在研究的简单脚本语言 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;
    }
}

【问题讨论】:

  • why they are put into these specific colours - 因为它们很漂亮!
  • 搜索“mark and sweep white gray black”导致我:memorymanagement.org/glossary/t.html#tri-color.marking。该页面特别提到该算法的一个重要属性是它是“正确的”,所以我的猜测是天真的方法有一些边缘情况会中断。
  • 另外:en.wikipedia.org/wiki/Mark_and_sweep#Naive_mark-and-sweep 列出了幼稚方法的主要缺点,即如果不暂停进程就无法执行。
  • 我想我需要知道的是,考虑到我简单的黑白实现,使它成为三色 GC 会发生什么变化(除了标记状态的明显 GC 标志)?而不是仅仅以抽象的方式谈论它的页面。在我的实现中,我有两个“根集”,对吗? (堆栈和全局对象 - 对象是关联数组样式对象)。

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


【解决方案1】:

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

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

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

【讨论】:

  • 我正试图围绕算法如何在没有全面扫描的情况下工作。我的意思是,如果我有一个标记为灰色的聚合对象,但其中一个子对象标记为白色,如何避免遍历整个树以确保白色对象不在其他地方引用。不扫描整个引用层次结构如何摆脱这种情况并没有真正的意义。除非标记阶段只是增量并且必须在标记完全扫描后进行扫描?在这种情况下,您必须管理标记之间的新引用。
  • "除非标记阶段只是增量并且必须在标记完全扫描后才进行扫描?"当然,在增量标记和扫描 GC 中,算法仍然在完整标记阶段和完整扫描阶段之间交替。这就是标记和扫描的工作原理。我相信 Paul Wilson 的文章“单处理器垃圾收集技术”应该可以回答您的问题。
【解决方案2】:

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

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

我们可以改为对这三个集合使用显式数据结构。但是对于 stop-the-world 的 mark & sweep gc,这个实现要高效得多。

【讨论】:

    猜你喜欢
    • 2021-06-23
    • 1970-01-01
    • 2019-12-13
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多