如何在C ++中实现垃圾收集

时间:2011-02-15 21:41:11

标签: c++ garbage-collection

我在C中看到一些关于实现GC的帖子,有些人说这是不可能的,因为C是弱类型的。我想知道如何在C ++中实现GC。

我想要了解如何做到这一点。非常感谢你!

这是我朋友告诉我的彭博采访问题。那时他做得很糟糕。我们想知道您对此的看法。

6 个答案:

答案 0 :(得分:55)

C和C ++中的垃圾收集都是困难的主题,原因如下:

  1. 指针可以对整数进行类型转换,反之亦然。这意味着我可以拥有一块只能通过获取整数,将其类型转换为指针,然后取消引用它的内存块。垃圾收集者必须小心,不要认为当一个块仍然无法访问时就无法访问。

  2. 指针不是不透明的。许多垃圾收集器,如停止和复制收集器,喜欢移动内存块或压缩它们以节省空间。由于您可以在C和C ++中显式查看指针值,因此很难正确实现。你必须确定如果有人通过对整数进行类型转换来做一些棘手的事情,那么如果你移动了一块内存,你就会正确地更新整数。

  3. 可以明确地进行内存管理。任何垃圾收集器都需要考虑用户能够随时明确释放内存块。

  4. 在C ++中,分配/释放与对象构造/销毁之间存在分离。可以分配具有足够空间的存储器块来保持对象,而不在那里实际构造任何对象。一个好的垃圾收集器在收回内存时需要知道是否为可能在那里分配的任何对象调用析构函数。对于标准库容器尤其如此,由于效率原因,它通常使用std::allocator来使用此技巧。

  5. 可以从不同区域分配内存。 C和C ++可以从内置的freestore(malloc / free或new / delete)获取内存,也可以通过mmap或其他系统调用从操作系统获取内存,对于C ++,可以从{{1 }或get_temporary_buffer。程序也可能从某些第三方库中获取内存。一个好的垃圾收集器需要能够跟踪这些其他池中对内存的引用,并且(可能)必须负责清理它们。

  6. 指针可以指向对象或数组的中间位置。在许多垃圾收集语言(如Java)中,对象引用始终指向对象的开头。在C和C ++中,指针可以指向数组的中间,而在C ++中指向对象的中间(如果使用多重继承)。这可能会使用于检测仍然可以访问的内容的逻辑变得非常复杂。

  7. 因此,简而言之,为C或C ++构建垃圾收集器非常困难。大多数在C和C ++中进行垃圾收集的库在它们的方法中非常保守,并且在技术上不合理 - 例如,他们假设您不会使用指针,将其转换为整数,将其写入磁盘,然后加载它稍晚回来了。他们还假设内存中任何指针大小的值都可能是一个指针,因此有时会拒绝释放无法访问的内存,因为有一个非零的机会指向它。

    正如其他人所指出的那样,Boehm GC确实为C和C ++做了垃圾收集,但受上述限制的约束。

    有趣的是,C ++ 11包含一些新的库函数,允许程序员将内存区域标记为可达且无法访问,以预期将来的垃圾收集工作。将来有可能用这种信息构建一个非常好的C ++ 11垃圾收集器。与此同时,你需要非常小心,不要违反任何上述规则。

答案 1 :(得分:4)

C不是C ++,但两者都有相同的“弱类型”问题。然而,这不是导致问题的隐式类型转换,而是“惩罚”(颠覆类型系统)的趋势,特别是在数据结构库中。

那里有 垃圾收集器用于C和/或C ++。 Boehm保守的收藏家可能是最了解的。它是保守的,如果它看到一个看起来像某个对象的指针的位模式,它就不会收集该对象。该值可能完全是其他类型的值,因此可以收集对象,但“保守”意味着可以安全地使用。

如果使用计算指针,即使是保守的收藏家也会被愚弄。例如,有一个数据结构,其中每个列表节点都有一个字段,给出下一个节点和前一个节点地址之间的差异。这个想法是给每个节点一个链接提供双链表行为,代价是更复杂的迭代器。由于大多数节点都没有明确的指针,因此可能会错误地收集它们。

当然这是一个非常特殊的特例。

更重要的是 - 你可以拥有可靠的析构函数或垃圾收集,而不是两者兼而有之。收集垃圾循环时,收集器无法决定首先调用哪个析构函数。

由于RAII模式在C ++中普遍存在,并且依赖于析构函数,因此IMO存在冲突。可能存在有效的异常,但我的观点是,如果你想要垃圾收集,你应该使用一种从头开始设计用于垃圾收集的语言(Java,C#,......)。

答案 2 :(得分:3)

答案 3 :(得分:3)

您可以使用智能指针或创建自己的容器对象,它将跟踪引用并处理内存分配等。智能指针可能更可取。通常,您可以完全避免动态堆分配。

例如:

char* pCharArray = new char[128];
// do some stuff with characters
delete [] pCharArray;

如果在新删除和删除之间抛出任何内容,则上述存在的危险将不会被执行。像上面这样的东西很容易被更安全的“垃圾收集”代码替换:

std::vector<char> charArray;
// do some stuff with characters
从实际的编码角度来看,彭博与众所周知的无关紧要的面试问题。像大多数面试官一样,他们主要关注的是你的思考方式和沟通技巧,而不是实际的解决方案。

答案 4 :(得分:0)

你看到的说法是假的; Boehm collector支持C和C ++。我建议阅读Boehm收集器的文档(特别是this page),以便很好地概述如何用C或C ++编写垃圾收集器。

答案 5 :(得分:0)

您可以阅读shared_ptr结构。

它实现了一个简单的reference-counting垃圾收集器。

如果你想要一个真正的垃圾收集器,你可以重载 new 运算符。

创建一个类似于shared_ptr的结构,称之为Object。

这将包装创建的新对象。现在,通过重载其运算符,您可以控制GC。

现在你需要做的只是实现众多GC algorithms

中的一个