编译语言中垃圾回收的实现

14
当实现精确垃圾回收时,总是需要解决如何确定堆栈上的哪些单词是指针,哪些是其他类型的数据,例如整数或浮点数的问题。解释性语言通常通过将所有内容都设为指针来解决这个问题;而对于一些语言(例如Lisp),编译器则通常使用标记位来区分指针和整数。但对于支持完全未装箱机器字长整数和浮点数的Java和C#等语言的JIT编译器,它们如何知道堆栈和CPU寄存器中的哪些内容是指针呢?

4
为什么会有关闭投票?这是个真正的问题。至于可能的实现方式——类型信息是静态可用的,所以所有指针都可以放入单独的堆栈中。 - SK-logic
3
并附相关链接:http://www.mono-project.com/Generational_GC#Precise_Stack_Marking - SK-logic
2个回答

10
这类语言的字节码始终包含完整的类型信息,它们存储在元数据中(例如用于参数类型)或者隐含在操作码中(例如可能有不同的操作码用于添加整数或浮点数)。编译器在优化代码时可以访问这些信息并使用它来改进优化。它还使用这些信息在特定的“GC安全点”为编译代码生成元数据。GC安全点是代码中的一个位置,在该位置可以安全地中断线程以调度另一个线程或执行垃圾回收。在GC安全点,我们可用于查找哪些寄存器包含指针而哪些不包含指针所需的元数据。例如,在Hotspot JVM中,循环始终包含对内存中特定位置的读取。该读取的结果没有使用,但如果指令读取的地址被保护,则会发生页错误。这可以通过将该页面设置为只读来在任意时间中断线程。一旦线程中断,我们就查看程序计数器并在哈希表中查找元数据。其他需要成为GC安全点的地方是分配站点:分配可能失败并导致GC发生。您可以通过同时为多个对象分配内存来减少安全点数目。编辑:请注意,使用GC安全点只是众多选项之一。另一个选择,如SK-logic所提到的,是使用指针和非指针的不同堆栈。然后可以明确地知道一个堆栈的所有元素在GC期间需要遍历,但其他的则不需要。不过,您仍需注意寄存器中的指针。例如,每当寄存器中存在活动指针时,相同的指针也必须存在于堆栈中。

第三种选择是使用一个影子栈(shadow stack),其中包含指向真实栈上的栈根(stack roots)的指针链表。详细内容请参见Fergus Henderson的论文《在不合作的环境中进行准确的垃圾收集》(PDF)


0
Java和C#等语言的规范并不要求精确集合(precise collection)。实现可能会使用保守收集器,在其中类似指针的位模式被视为指针处理(但实际上可能是整数或浮点数)。例如,Boehm collector是一种保守的收集器,可用于即时编译语言。

1
当然,Mono曾经使用Boehm。但是典型的实现(Sun Java、Microsoft .Net、最新版本的Mono等)确实使用精确垃圾回收。我对此很感兴趣。 - rwallace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接