基本上,我有兴趣在 C 中编写独立于平台的垃圾收集器,可能使用标记和扫描算法或其常见变体之一。理想情况下,界面将按以下方式工作:
(1)gc_alloc()
分配内存
(2)gc_realloc()
重新分配内存
(3)gc_run()
运行垃圾收集器。
我已经看过Boehm等人开发的libgc
垃圾收集库。 al。,但它不是平台独立的;它刚刚移植到许多不同的系统。我想实现一个不包含系统相关代码的垃圾收集器。速度不是巨大的问题。
有什么建议吗?
答案 0 :(得分:10)
不幸的是,实际上不可能在C中创建一个真正平台独立的垃圾收集器。严格读取C标准允许任何类型(除了unsigned char
)都有陷阱位 - 当它们具有错误值时,导致系统发出异常信号(即,未定义的行为)的位。扫描已分配的指针块时,您无法确定特定的内存块是否包含合法的指针值,或者在您尝试查看其中的值时它是否会陷阱。
将指针检查为int也无济于事 - 不需要int类型来使表示与指针兼容。 intptr_t
仅适用于最近的编译器,我不认为其表示也需要兼容。而且int也可以有陷阱位。
您也不知道指针的对齐要求。在指针没有对齐要求的平台上(即,可以从任何字节开始),这意味着您需要在每个字节memcpy
处停止到合适的指针类型,并检查结果。哦,不同的指针类型也可以有不同的表示形式,这也是不可避免的。
但更大的问题是找到根集。 Bohem GC和其他人倾向于扫描堆栈以及静态数据,以获取应该放在根集中的指针。 如果不了解操作系统的内存布局,这是不可能的。因此,您需要让用户明确标记根集的成员,这会破坏垃圾收集器的点。
因此,简而言之,您无法在真正的便携式C中制作GC。原则上,如果您做出一些假设,则可以这样做:
intptr_t
可用或假设所有void *
都是严格排序的(即<
和>
合理地使用来自不同的指针malloc
ations)void *
兼容的表示。memcpy
指针一个已知对齐的位置,并且还会减少要检查的潜在指针的数量。如果你做出这些假设,你应该能够制作一个保守的标记扫描分配器。使用二叉树来保存有关分配位置的信息,并扫描分配的指针块中每个可能的对齐指针位置。但是,显式提供根集的需要将使这一切毫无意义 - 它将再次malloc
和free
,除了对于某些不明确定义的对象集合,您可以跳过它。不完全是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
*/