我看到一些关于用C实现GC帖子,有些人说这是不可能的,因为C是弱类型的。我想知道如何在C++中实现GC。我想知道如何做这件事的大致想法。非常感谢!这是我朋友告诉我的一个彭博社的采访问题,他当时做得很差,我们想知道你对这个问题的看法。
ee7vknir1#
C和C中的垃圾回收都是困难的主题,原因如下:1.指针可以被类型转换为整数,反之亦然。这意味着我可以有一个内存块,它只能通过取一个整数,将它类型转换为指针,然后取消引用它来访问。垃圾收集器必须小心,不要认为一个块是不可访问的,而实际上它仍然可以访问。1.指针并不是不透明的。许多垃圾收集器,比如停止并复制收集器,喜欢移动内存块或压缩内存块以保存空间。由于在C和C中可以显式查看指针值,这可能难以正确地实现。您必须确保,如果有人在对整数进行类型转换时做了一些棘手的事情,那么在移动记忆。1.内存管理可以显式地完成。任何垃圾收集器都需要考虑到用户能够在任何时候显式地释放内存块。1.在C中,分配/解除分配和对象构造/析构是分开的。一个内存块可以被分配足够的空间来容纳一个对象,而不需要在那里实际构造任何对象。一个好的垃圾收集器需要知道,当它回收内存时,是否为可能分配到那里的任何对象调用析构函数。2这对于标准库容器尤其如此,其经常利用std::allocator来使用该技巧。1.内存可以从不同的区域分配。C和C可以从内置的freestore(malloc/free或new/delete),或者通过mmap或其他系统调用从OS调用,并且,在C的情况下,从get_temporary_buffer或return_temporary_buffer获取内存。程序也可能从某些第三方库获取内存。一个好的垃圾收集器需要能够跟踪对这些其他池中的内存的引用,并且(可能)必须负责清理它们。1.指针可以指向对象或数组的中间。在许多垃圾收集语言(如Java)中,对象引用总是指向对象的开头。在C和C中,指针可以指向数组的中间,而在C中,指针可以指向对象的中间(如果使用了多重继承)。这会使检测仍然可达的对象的逻辑变得非常复杂。因此,简而言之,为C或C构建一个垃圾收集器是极其困难的。大多数在C和C中进行垃圾收集的库在方法上都是极其保守的,然后在以后的某个时间再把它加载回去。他们还假设内存中任何一个指针大小的值都可能是指针,因此有时会拒绝释放不可访问的内存,因为存在指向它的指针的可能性不为零。正如其他人所指出的,Boehm GC确实为C和C执行垃圾收集,但受到上述限制。有趣的是,C11包含了一些新的库函数,它们允许程序员标记内存区域为可达和不可达,以备将来进行垃圾收集。将来有可能用这类信息构建一个真正好的C11垃圾收集器。但同时,你需要非常小心,不要违反上述任何规则。
std::allocator
mmap
get_temporary_buffer
return_temporary_buffer
r7knjye22#
查看Boehm Garbage Collector。
aiazj4mn3#
C不是C++,但两者都有同样的“弱类型化”问题。虽然不是隐式类型转换造成的问题,而是倾向于“双关”(破坏类型系统),特别是在数据结构库中。C和/或C有很多垃圾收集器。Boehm保守收集器可能是最有名的。它的保守之处在于,如果它看到一个看起来像指向某个对象的指针的位模式,它不会收集该对象。该值可能完全是其他类型的值,因此该对象可以被收集,但“保守”意味着谨慎行事。但是,如果使用计算指针,即使是保守的收集器也会被愚弄。例如,有一个数据结构,其中每个列表节点都有一个字段,用于提供下一个节点和上一个节点地址之间的差异。其思想是以更复杂的迭代器为代价,给予每个节点一个链接的双链表行为。由于大多数节点都没有显式指针,它们可能被错误地收集。当然这是一个非常例外的特例。更重要的是,你可以选择可靠的析构函数或者垃圾回收,但不能两者都选。当垃圾循环被回收时,回收器不能决定首先调用哪个析构函数。由于RAII模式在C中很普遍,并且依赖于析构函数,因此存在冲突。可能会有有效的异常,但我的观点是,如果你想进行垃圾收集,你应该使用一种从头开始为垃圾收集设计的语言(Java、C#...)。
u4vypkhs4#
你可以使用智能指针或者创建你自己的容器对象来跟踪引用和处理内存分配等。智能指针可能会更好。通常你可以完全避免动态堆分配。例如:
char* pCharArray = new char[128]; // do some stuff with characters delete [] pCharArray;
上述代码的危险之处在于,如果在new和delete之间有任何东西抛出,那么您的delete将不会被执行。类似上述代码可以很容易地用更安全的 “垃圾收集” 代码来替换:
std::vector<char> charArray; // do some stuff with characters
众所周知,彭博社的面试问题与实际编程无关。与大多数面试官一样,他们主要关心的是你的思维方式和沟通技巧,而不是实际的解决方案。
roqulrg35#
您可以阅读有关shared_ptr结构的内容。它实现了一个简单的reference-counting垃圾收集器。如果您需要一个真实的的垃圾收集器,可以重载new运算符。创建一个类似shared_ptr的结构,将其命名为Object。这将 Package 创建的新对象。现在通过重载它的操作符,你可以控制GC。您现在需要做的就是实现许多GC algorithms中的一个
ia2d9nvy6#
你看到的说法是假的; Boehm collector支持C和C++。我建议阅读Boehm收集器的文档(特别是this page),以了解如何用C或C++编写垃圾收集器。
6条答案
按热度按时间ee7vknir1#
C和C中的垃圾回收都是困难的主题,原因如下:
1.指针可以被类型转换为整数,反之亦然。这意味着我可以有一个内存块,它只能通过取一个整数,将它类型转换为指针,然后取消引用它来访问。垃圾收集器必须小心,不要认为一个块是不可访问的,而实际上它仍然可以访问。
1.指针并不是不透明的。许多垃圾收集器,比如停止并复制收集器,喜欢移动内存块或压缩内存块以保存空间。由于在C和C中可以显式查看指针值,这可能难以正确地实现。您必须确保,如果有人在对整数进行类型转换时做了一些棘手的事情,那么在移动记忆。
1.内存管理可以显式地完成。任何垃圾收集器都需要考虑到用户能够在任何时候显式地释放内存块。
1.在C中,分配/解除分配和对象构造/析构是分开的。一个内存块可以被分配足够的空间来容纳一个对象,而不需要在那里实际构造任何对象。一个好的垃圾收集器需要知道,当它回收内存时,是否为可能分配到那里的任何对象调用析构函数。2这对于标准库容器尤其如此,其经常利用
std::allocator
来使用该技巧。1.内存可以从不同的区域分配。C和C可以从内置的freestore(malloc/free或new/delete),或者通过
mmap
或其他系统调用从OS调用,并且,在C的情况下,从get_temporary_buffer
或return_temporary_buffer
获取内存。程序也可能从某些第三方库获取内存。一个好的垃圾收集器需要能够跟踪对这些其他池中的内存的引用,并且(可能)必须负责清理它们。1.指针可以指向对象或数组的中间。在许多垃圾收集语言(如Java)中,对象引用总是指向对象的开头。在C和C中,指针可以指向数组的中间,而在C中,指针可以指向对象的中间(如果使用了多重继承)。这会使检测仍然可达的对象的逻辑变得非常复杂。
因此,简而言之,为C或C构建一个垃圾收集器是极其困难的。大多数在C和C中进行垃圾收集的库在方法上都是极其保守的,然后在以后的某个时间再把它加载回去。他们还假设内存中任何一个指针大小的值都可能是指针,因此有时会拒绝释放不可访问的内存,因为存在指向它的指针的可能性不为零。
正如其他人所指出的,Boehm GC确实为C和C执行垃圾收集,但受到上述限制。
有趣的是,C11包含了一些新的库函数,它们允许程序员标记内存区域为可达和不可达,以备将来进行垃圾收集。将来有可能用这类信息构建一个真正好的C11垃圾收集器。但同时,你需要非常小心,不要违反上述任何规则。
r7knjye22#
查看Boehm Garbage Collector。
aiazj4mn3#
C不是C++,但两者都有同样的“弱类型化”问题。虽然不是隐式类型转换造成的问题,而是倾向于“双关”(破坏类型系统),特别是在数据结构库中。
C和/或C有很多垃圾收集器。Boehm保守收集器可能是最有名的。它的保守之处在于,如果它看到一个看起来像指向某个对象的指针的位模式,它不会收集该对象。该值可能完全是其他类型的值,因此该对象可以被收集,但“保守”意味着谨慎行事。
但是,如果使用计算指针,即使是保守的收集器也会被愚弄。例如,有一个数据结构,其中每个列表节点都有一个字段,用于提供下一个节点和上一个节点地址之间的差异。其思想是以更复杂的迭代器为代价,给予每个节点一个链接的双链表行为。由于大多数节点都没有显式指针,它们可能被错误地收集。
当然这是一个非常例外的特例。
更重要的是,你可以选择可靠的析构函数或者垃圾回收,但不能两者都选。当垃圾循环被回收时,回收器不能决定首先调用哪个析构函数。
由于RAII模式在C中很普遍,并且依赖于析构函数,因此存在冲突。可能会有有效的异常,但我的观点是,如果你想进行垃圾收集,你应该使用一种从头开始为垃圾收集设计的语言(Java、C#...)。
u4vypkhs4#
你可以使用智能指针或者创建你自己的容器对象来跟踪引用和处理内存分配等。智能指针可能会更好。通常你可以完全避免动态堆分配。
例如:
上述代码的危险之处在于,如果在new和delete之间有任何东西抛出,那么您的delete将不会被执行。类似上述代码可以很容易地用更安全的 “垃圾收集” 代码来替换:
众所周知,彭博社的面试问题与实际编程无关。与大多数面试官一样,他们主要关心的是你的思维方式和沟通技巧,而不是实际的解决方案。
roqulrg35#
您可以阅读有关shared_ptr结构的内容。
它实现了一个简单的reference-counting垃圾收集器。
如果您需要一个真实的的垃圾收集器,可以重载new运算符。
创建一个类似shared_ptr的结构,将其命名为Object。
这将 Package 创建的新对象。现在通过重载它的操作符,你可以控制GC。
您现在需要做的就是实现许多GC algorithms中的一个
ia2d9nvy6#
你看到的说法是假的; Boehm collector支持C和C++。我建议阅读Boehm收集器的文档(特别是this page),以了解如何用C或C++编写垃圾收集器。