垃圾收集器如何遍历(动态)内存?

时间:2010-08-10 10:20:58

标签: memory garbage-collection

我已经了解了不同种类的GC以及它们的工作原理。所有这些都涉及遍历可能被回收的内存集,但我读过的内容并没有给出任何关于如何实际完成的指示。像下面这样的东西会不合时宜吗?

void* myAlloc(size_t size)
{
    if (needToGc())
        gc();
    void* obj = malloc(size);
    if (!obj)
        outOfMemory(); // or maybe GC and try once more
    *alloced = obj;
    *alloced = *alloced + 1;
    return obj;
}
void gc()
{
    // go through memory pointed to in `alloced`
}

我怀疑是这样,但这是唯一让我想到的事情......

1 个答案:

答案 0 :(得分:1)

垃圾收集器有很多种,具体取决于要求(内存开销,延迟,数据布局......)。我的一些答案可能不适用于一些复杂的收藏家。

首先,不需要needToGc:如果malloc失败,则会触发垃圾收集器。

关于如何遍历内存,垃圾收集器的任务是将正在使用的内存与未使用的内存分开,并回收后者。基本原则是如果可以从程序访问内存,则使用内存。确定如下:

  • 某些 root 对象被视为可访问。例如,堆栈上的任何内容都是可访问的,并且可以访问任何全局变量。

  • 如果可到达的对象指向另一个对象,则该另一个对象也可以访问。

  • 遗留下来的任何东西都无法到达,因此被视为未使用且可回收。

当触发垃圾收集器时,它会遍历所有根对象,并且每个对象递归遍历可到达的对象。收集器遍历所有可到达的对象后,它将回收未遍历的对象。

这种遍历的简单技术称为标记和扫描。每个对象都包含一个最初为0的标记位。当遍历函数到达一个对象时,它会查看标记位:如果标记位为1,则该对象已被遍历;如果标记位为0,则遍历函数将其设置为1,并在当前对象指向的每个对象上递归调用自身。标记阶段包括为每个根对象调用遍历函数。接下来是扫描阶段,它遍历所有已分配的对象:如果对象仍然标记为0,则释放它;否则其标记重置为0。

那里的大多数垃圾收集器都基于标记和扫描,但通常会有相当大的复杂性。

有关更多信息和其他指示,请参阅wikipedia article