我检查了 Boehm GC。C/C++ 的 GC。
我知道标记和扫描算法。我很好奇的是它如何只获取整个 C 内存中的指针。我对 C 内存的理解只是一个普通的字节数组。是否可以确定内存中的值是否为指针?
我检查了 Boehm GC。C/C++ 的 GC。
我知道标记和扫描算法。我很好奇的是它如何只获取整个 C 内存中的指针。我对 C 内存的理解只是一个普通的字节数组。是否可以确定内存中的值是否为指针?
Boehm GC 是一个保守的收集器,这意味着它假定一切都是指针。这意味着它可以找到误报引用,例如恰好具有堆中地址值的整数。因此,某些块在内存中的停留时间可能比使用非保守收集器的时间长。
这是Boehm 页面的描述:
垃圾收集器使用修改后的标记清除算法。从概念上讲,它大致分为四个阶段,这些阶段偶尔会作为内存分配的一部分执行:
- 准备 每个对象都有一个关联的标记位。清除所有标记位,表示所有对象都可能无法访问。
- 标记阶段标记可以通过变量的指针链访问的所有对象。收集器通常没有关于堆中指针变量位置的真实信息,因此它将所有静态数据区域、堆栈和寄存器视为可能包含指针。任何表示收集器管理的堆对象内部地址的位模式都被视为指针。除非客户端程序已将堆对象布局信息提供给收集器,否则任何发现可从变量访问的堆对象都会再次被类似地扫描。
- 扫描阶段扫描堆中不可访问的,因此未标记的对象,并将它们返回到适当的空闲列表以供重用。这实际上不是一个单独的阶段。即使在非增量模式下,这通常也是在发现空空闲列表的分配期间按需执行的操作。因此,扫描阶段不太可能触及此后不久不会触及的页面。
- 完成阶段 已注册完成的无法访问的对象在收集器之外排队等待完成。
您还应该知道 Boehm GC 需要被赋予一组“根”,它们是标记和扫描算法的起点。堆栈和寄存器自动成为根。您需要将全局指针显式添加为根。
编辑:在评论中,人们普遍对保守的收藏家提出了一些担忧。确实,看起来像指向收集器的堆指针的整数会导致内存不被释放。这不是你想象的那么大的问题。程序中的大多数标量整数用于计数和大小,并且相当小(因此它们看起来不像堆指针)。您通常会遇到包含位图、字符串、浮点数据或任何类似内容的数组的问题。Boehm GC 允许您分配一个块,GC_MALLOC_ATOMIC
向收集器指示该块将不包含任何指针。如果您查看gc_typed.h,您还将找到指定块的哪些部分可能包含指针的方法。
That said, a fundamental limitation of a conservative collector is that it cannot safely move memory around during collection since pointer rewriting is not safe. This means you won't get any of the benefits of compaction like lowered fragmentation and improved cache performance.