【问题标题】:Garbage collection when compiling to C编译为 C 时的垃圾收集
【发布时间】:2011-01-05 11:50:00
【问题描述】:

在将垃圾收集语言编译成 C 时,垃圾收集的技术是什么?我知道两个:

  1. 维护一个影子堆栈,将所有根显式保存在数据结构中

  2. 使用像 Boehm 这样的保守垃圾收集器

第一种技术很慢,因为您必须维护影子堆栈。可能每次调用函数时,您都需要将局部变量保存在数据结构中。

第二种技术也很慢,并且由于使用了保守的垃圾收集器,本质上不会回收所有垃圾。

我的问题是:编译为 C 时垃圾收集的最新技术是什么。请注意,我并不是指在 C 中编程时进行垃圾收集的便捷方式(这是 Boehm 垃圾收集器的目标), 编译到 C 时只是一种进行垃圾收集的方法。

【问题讨论】:

  • 这是一个讨厌的。这也是为什么例如LLVM 和 C-- 很受欢迎(它们允许垃圾收集而无需手动处理)。
  • 原始语言中的类型系统是基于有向无环图还是通用图? DAG 只需要引用计数(由于是非循环的)。
  • 一般图表,很遗憾。我也考虑过引用计数,但这似乎并不能解决所有问题,因为您仍然需要一种方法来遍历根以收集循环......
  • 编译 C是什么意思?能给个示例命令吗?

标签: c compiler-construction garbage-collection


【解决方案1】:

可能每次调用函数时,都需要将局部变量保存在数据结构中。

不,你不需要 - 你可以将局部变量留在 C 堆栈上并仍然遍历它们:将所有引用变量放在一个数组中,并将指向该数组的指针添加到一个链表,当你在该链表中添加一个节点时进入一个新的堆栈帧。

样机:

struct vm
{
    struct scope *root;
};

struct scope
{
    struct scope *prev, *next;
    size_t size;
    struct ref *refs;
};

void foo(struct vm *vm, struct scope *caller)
{
    struct ref local_refs[42];
    struct scope scope = {
        caller, NULL, sizeof local_refs / sizeof *local_refs, local_refs };

    caller->next = &scope;

    // ...

    caller->next = NULL;
}

但是,如果您想支持延续/非局部跳转,则必须跳过一些主要的障碍。在这种情况下,堆分配所有内容会更容易。

【讨论】:

  • 当您使用 C 作为目标语言来编译其他语言时,这种技术最有用,因为它容易出错并且需要仔细跟踪所有内容
  • 谢谢,这是一个有趣的技术。然而,这是否意味着局部变量不存在于寄存器中,这很可能比在调用时保存本地变量还要慢?
  • @Jules:编译器仍然可以在寄存器中缓存任何本地值以进行计算,但你是对的,它必须在函数调用时同步值;我将不得不考虑如何通过告诉编译器调用的函数不会修改局部变量来适当地使用 constrestrict 来改善这种情况......
猜你喜欢
  • 2021-05-28
  • 1970-01-01
  • 1970-01-01
  • 2013-04-01
  • 1970-01-01
  • 2011-10-29
  • 1970-01-01
  • 2023-03-24
  • 2021-02-05
相关资源
最近更新 更多