如果你想要一个精确 GC(不是保守的,像Boehm's GC,它在实践中表现很好)你应该跟踪本地指针(指向GC-ed数据)变量,否则当您确定没有这样的局部变量时,仅使用几乎为空的调用堆栈调用 GC(顺便说一句,GCC 编译器有这样的mark&sweep garbage collector - 带有由一些专门的gengtype C++ 代码生成器生成的标记例程;那GGC 仅在 之间 次传递中调用)。当然,您还应该跟踪全局(包括静态或线程本地)指针(指向 GC 数据)变量。
或者,有一些字节码虚拟机(如OCaml 或NekoVM 有),然后本地GC-ed 变量是您的字节码VM 的堆栈和/或寄存器中的变量,并且您在特定位置触发GC以及精心挑选的 VM 解释器要点。 (参见 Ocaml GC 的 this explanation)。
您应该阅读有关Garbage Collection 技术的更多信息,请参阅GC handbook。
如果您的 GC 是分代复制,您需要实现写屏障(以处理指向新区域的旧数据的突变)。你可以使用我旧的Qish GC(我不再维护它),或者Ravenbrook's MPS,或者编写你自己的分代复制GC(这在理论上并不难,但在实践中调试GC是一场噩梦,所以工作量很大)。
您可能想使用一些宏技巧(例如我的 Qish 所做的)来帮助保留局部变量。请参阅 Ocaml 文档的 Living in harmony with the garbage collector 部分作为示例(或查看 Qish 内部)。
请注意,在手动编写的 C 代码中处理分代复制 GC 并不友好(因为您需要显式保留本地指针,并且因为您需要写屏障来记住何时修改旧值以具有指向新一代)。如果你想这样做,你的C代码应该在A-normal form中(你不能编码x=f(g(y),z);,但你需要编码temp=g(y); x=f(temp,z);并添加temp作为局部变量,假设x,y , z 是本地 GC 变量,f 和 g 返回一个 GC 指针)。实际上,生成 C 代码要容易得多。以我的MELT 域特定语言(扩展和自定义GCC)为例。
如果您的语言是真正的多线程语言(多个 mutator 线程并行分配),那么编写 GC 代码就变得非常棘手。这可能需要几个月的工作(而且调试起来可能是一场噩梦)。
实际上,我今天推荐使用 Boehm 的 GC(注意它是多线程友好的)。一个简单的 mark&sweep 手工编码的 GC 可能不会比 Boehm 的 GC 快。而且您将无法(我不推荐)使用 GGC,GCC 内部的垃圾收集器(IMNSHO,它不是很好;多年前这是一个肮脏的 hack 设计)。
顺便说一句,您可能会考虑 自定义 -e.g.使用MELT- GCC 编译器(通过添加一些特定于应用程序的__attribute__ 或#pragma)来帮助您的GC。通过一些工作,您可以生成一些标记例程等。但是,这种方法可能非常痛苦(我真的不知道)。请注意,MELT(免费软件,GPLv3+)包含一个复制的分代 GC,其老年代是 GGC 堆,因此您至少可以查看code of melt-runtime.cc 的内部
PS。我也推荐Queinnec的书:Lisp In Small Pieces;它有一些关于 GC 及其与编程语言的联系的有趣材料,当您实现解释器时,这是一本很棒的书。 Scott 在Programming Languages Pragmatics 上的书也值得一读。