Boehm GC如何为C程序工作?

Eon*_*nil 14 c garbage-collection principles boehm-gc

我检查了Boehm GC.用于C/C++的GC.

我知道标记和扫描算法.我很好奇的是它如何只在整个C内存中获取指针.我对C内存的理解只是一个普通的字节数组.是否有可能确定内存中的值是否为指针?

Jay*_*rod 18

Boehm GC是一个保守的收集器,这意味着它假设一切都是指针.这意味着它可以找到误报引用,就像一个巧合地具有堆中地址值的整数.结果,一些块可能比非保守的收集器在内存中停留的时间更长.

以下是Boehm页面的描述:

垃圾收集器使用修改的标记扫描算法.从概念上讲,它大致分四个阶段运行,偶尔作为内存分配的一部分执行:

  1. 准备每个对象都有一个关联的标记位.清除所有标记位,表示所有对象都可能无法访问.
  2. 标记阶段标记可通过变量指针链到达的所有对象.收集器通常没有关于堆中指针变量位置的真实信息,因此它将所有静态数据区域,堆栈和寄存器视为可能包含指针.表示收集器管理的堆对象内的地址的任何位模式都被视为指针.除非客户端程序已将堆对象布局信息提供给收集器,否则将再次以类似方式扫描发现可从变量访问的任何堆对象.
  3. 扫描阶段扫描堆中的不可访问的,因此未标记的对象,并将它们返回到适当的空闲列表以供重用.这不是一个单独的阶段; 即使在非增量模式下,这也是通常在发现空闲列表的分配期间按需执行操作.因此,扫描阶段不太可能触及此后不久就不会被触摸的页面.
  4. 结束阶段已注册完成的无法访问的对象将排队,以便在收集器外部完成.

您还应该知道Boehm GC需要给出一组"根",它们是标记和扫描算法的起点.堆栈和寄存器是自动根.您需要显式添加全局指针作为根.


编辑:在评论中,一般关注保守收藏家的一些担忧.确实,看起来像收集器的堆指针的整数会导致内存不被释放.这并不像你想象的那么大.程序中的大多数标量整数用于计数和大小,并且相当小(因此它们看起来不像堆指针).您将主要遇到包含位图,字符串,浮点数据或任何类型的数组的问题.Boehm GC允许您分配一个块,GC_MALLOC_ATOMIC向收集器指示该块不包含任何指针.如果查看gc_typed.h,您还可以找到指定块的哪些部分可能包含指针的方法.

也就是说,保守收集器的一个基本限制是它在收集过程中不能安全地移动内存,因为指针重写是不安全的.这意味着您将无法获得压缩的任何好处,例如降低碎片和提高缓存性能.

  • @codymanix:您的评论是我每日"我希望我可以向下投票评论"比赛的最新条目.从它的"质量"来看,你很有可能成为当天的赢家,如果不是本周的话. (11认同)
  • 如果没有编译器的帮助(告诉你指针在哪里),它可能会得到很好的结果. (7认同)
  • @codymanix:根据该定义,所有GC算法都是垃圾,因为在某些情况下它们都会泄漏内存.一般来说,泄漏的垃圾量是有限的(不会随着时间的推移而增长),仔细的编程可以避免/减少,所以它可以正常工作. (7认同)
  • 大声笑,如果这是真的,这个算法是普通的垃圾xD (2认同)
  • @sbi:我只是说一个不起作用的GC(内存泄漏)毫无价值。如果有 GC,您一定可以信赖它。如果没有,事情会变得更糟,因为程序员开始省略对 free() 的调用,因为“当我测试它时它起作用了”。 (2认同)
  • @ChrisDodd:RAII 比跟踪具有所有者的对象的 GC 更好。对于没有所有者的不可变对象,跟踪 GC 优于 RAII 或引用计数。在不使用跟踪 GC 的多线程代码中,确定对不可变字符串的最后一个引用何时被销毁需要在可能使用它的所有线程之间进行同步。在跟踪 GC 中,在线程之间共享字符串引用只需要物理复制引用并让代码包含元数据以指示哪些 CPU 寄存器或堆栈插槽在何时保存引用。 (2认同)
  • @supercat:您似乎将复制收集器与跟踪收集器混为一谈。标记清除跟踪收集器具有所有相同的优点,但压缩除外。压缩所节省的内存是一个很小的常数因子,通常小于 10%。2 空间复制收集器的开销是 100% 您可以通过使用 mark-compact 而不是简单复制来减少开销,但这有其自身的成本。最重要的是,来自保守收藏家(如 Boehm)的“额外开销”几乎消失在噪音中。 (2认同)