我正在C ++ 0x中实现一个压缩垃圾收集器供我个人使用,我有一个问题。显然,收集器的机制取决于移动对象,我一直想知道如何根据指向它的智能指针类型来实现它。我一直在考虑指针类型本身的指针指针,或者,收集器维护一个指向每个对象的指针列表,以便可以修改它们,从而在访问时无需双重de-ref指针但在收集过程中增加了一些额外的开销和额外的内存开销。去哪里最好的方法是什么?
编辑:我主要关心的是快速分配和访问。我并不关心特别有效的收藏或其他维护,因为这不是GC的目的。
答案 0 :(得分:11)
关于将额外的GC移植到C ++,没有什么是直接的,更不用说压缩算法了。目前尚不清楚你正在尝试做什么以及它将如何与其余的C ++代码进行交互。
我实际上用C ++编写了一个gc,它与现有的C ++代码一起使用,并且它在一个阶段有一个压缩器(虽然我放弃它因为它太慢了)。但是有许多令人讨厌的语义问题。我几周前才向Bjarne提到C ++缺乏正确执行它的操作员,情况是它不可能存在,因为它的实用性有限..
您实际需要的是“重新添加我”操作员。发生的事情是你实际上没有移动物体。您只需使用mmap更改对象地址。这要快得多,实际上,它使用VM功能来提供句柄。
如果没有这个工具,你必须有办法执行一个对象的重叠移动,这是你无法有效地在C ++中完成的:你必须首先移动到临时。在C中,它更容易,您可以使用memmove
。在某个阶段,必须调整所有指向或移动对象的指针。
使用句柄不解决了这个问题,它只是将任意大小的对象的问题减少到常量大小的对象:这些在数组中更容易管理,但存在同样的问题:你必须管理存储。如果从数组中随机删除大量句柄,则仍然存在碎片问题。
所以不要理会手柄,它们不起作用。
这就是我在Felix中所做的:你打电话给new(shape, collector) T(args)
。这里shape
是类型的描述符,包括包含(GC)指针的偏移列表,以及完成对象的例程的地址(默认情况下,它调用析构函数)。
它还包含一个标志,说明是否可以使用memmove
移动对象。如果对象很大或不可移动,则由malloc
分配。如果对象很小且移动,则在竞技场中分配它,前提是场地中有空间。
通过移动其中的所有对象来压缩竞技场,并使用形状信息全局调整指向这些对象的所有指针。压缩可以逐步完成。
C ++程序员的缺点是需要构造一个正确的shape
对象来传递。这并没有打扰我,因为我正在实现一种可以自动生成形状信息的语言。
现在:关键点是:要进行压缩,必须使用精确的收集器。压缩无法与保守的收集器一起使用。这是非常重要的。如果你看到一个看起来像指针但恰好是整数的值,允许一些泄漏是很好的:一些对象不会被收集,但这通常没什么大不了的。但是对于压缩,你必须调整指针,但你最好不要改变那个整数:所以你必须知道肯定什么是指针,所以你的收藏家有准确地说:必须知道形状。
在Ocaml中,这是相对简单的:一切都是指针或整数,并且在运行时使用低位来表示。指向的对象有一个告诉类型的代码,并且只有几种类型:标量(不扫描它)或聚合(扫描它,它只包含整数或指针)。
答案 1 :(得分:1)
这是一个非常直截了当的问题所以这是一个直截了当的答案:
Mark-and-sweep
(偶尔mark-and-compact
以避免堆碎片)在分配和访问方面是最快的(避免双重de-refs)。它也很容易实现。由于您并不担心收集性能影响(标记和扫描倾向于以不确定的方式冻结过程),因此应该采用这种方式。
实施细节见:
答案 2 :(得分:0)
托儿所生成将为您提供最佳的分配性能,因为它只是一个指针凹凸。
你可以通过使用像阴影堆栈这样的技术来实现指针更新,而不使用双重间接,但如果你手工编写这个C ++代码,这将很慢并且非常容易出错。