如何实现独立于平台的垃圾收集器?

时间:2011-02-11 12:46:45

标签: c garbage-collection

基本上,我有兴趣在 C 中编写独立于平台的垃圾收集器,可能使用标记和扫描算法或其常见变体之一。理想情况下,界面将按以下方式工作:

(1)gc_alloc()分配内存

(2)gc_realloc()重新分配内存

(3)gc_run()运行垃圾收集器。

我已经看过Boehm等人开发的libgc垃圾收集库。 al。,但它不是平台独立的;它刚刚移植到许多不同的系统。我想实现一个不包含系统相关代码的垃圾收集器。速度不是巨大的问题。

有什么建议吗?

2 个答案:

答案 0 :(得分:10)

不幸的是,实际上不可能在C中创建一个真正平台独立的垃圾收集器。严格读取C标准允许任何类型(除了unsigned char)都有陷阱位 - 当它们具有错误值时,导致系统发出异常信号(即,未定义的行为)的位。扫描已分配的指针块时,您无法确定特定的内存块是否包含合法的指针值,或者在您尝试查看其中的值时它是否会陷阱。

将指针检查为int也无济于事 - 不需要int类型来使表示与指针兼容。 intptr_t仅适用于最近的编译器,我不认为其表示也需要兼容。而且int也可以有陷阱位。

您也不知道指针的对齐要求。在指针没有对齐要求的平台上(即,可以从任何字节开始),这意味着您需要在每个字节memcpy处停止到合适的指针类型,并检查结果。哦,不同的指针类型也可以有不同的表示形式,这也是不可避免的。

但更大的问题是找到根集。 Bohem GC和其他人倾向于扫描堆栈以及静态数据,以获取应该放在根集中的指针。 如果不了解操作系统的内存布局,这是不可能的。因此,您需要让用户明确标记根集的成员,这会破坏垃圾收集器的点。

因此,简而言之,您无法在真正的便携式C中制作GC。原则上,如果您做出一些假设,则可以这样做:

  • 假设用户将明确地为您提供根集。
  • 假设指针或int表示中没有陷阱位。
  • 假设intptr_t可用假设所有void *都是严格排序的(即<>合理地使用来自不同的指针malloc ations)
  • 假设所有数据指针类型都具有与void *兼容的表示。
  • 可选,但速度提升:硬编码指针对齐(这远非通用,需要编译器和平台特定)这个假设可以让你跳过memcpy指针一个已知对齐的位置,并且还会减少要检查的潜在指针的数量。

如果你做出这些假设,你应该能够制作一个保守的标记扫描分配器。使用二叉树来保存有关分配位置的信息,并扫描分配的指针块中每个可能的对齐指针位置。但是,显式提供根集的需要将使这一切毫无意义 - 它将再次mallocfree,除了对于某些不明确定义的对象集合,您可以跳过它。不完全是GC应该提供的,但我想它可能有它的位置,例如,虚拟机的一部分(在这种情况下,根集将来自虚拟机可用的信息)。

请注意,这一切都只适用于保守的 GC - 即盲目工作,在不知道数据位置的情况下扫描数据中的指针。如果您正在使用VM,则更容易 - 您可以为VM的所有分配构建统一数据类型,以明确列出可以找到指针的位置。使用此加上一个显式的根集,您可以构建一个非保守的GC;这应该足以构建VM或解释器。

答案 1 :(得分:1)

对于标记和扫描算法,您真正需要做的就是计算哪些对象可以从根集中访问,对吧? (不久之前,我挖掘了这个...)

这可以通过GC管理对象的单独对象图进行管理,而您需要做的“全部”是添加函数,以便在分配或修改托管对象时正确管理该图形。 如果还为托管对象添加引用计数,则可以更容易地计算哪些可以直接从堆栈引用访问。

这应该可以编写相当独立于平台的,尽管这可能是一个真正的垃圾收集器可能是有争议的。

简单的伪代码,通过引用计数和图形管理显示我的意思:

some_object * pObject = gc_alloc(sizeof(some_object));
some_container * pContainer = gc_alloc(sizeof(some_container));

pContainer->pObject = pObject;
/* let the GC know that pContainer has a reference to pObject */
gc_object_reference(pContainer, pObject);

/* a new block of some kind */
{
    /* let the GC know we have a new reference for pObject */
    some_object * pReference = gc_reference(pObject); 

    /* do stuff */
    ...

    /* let the GC know that this reference is no longer used */
    gc_left_scope(pReference);
}

gc_left_scope(pObject);
gc_run(); /* should not be able to recycle anything, since there still is a live
           * reference to pContainer, and that has a reference to pObject
           */