Rust 中安全垃圾回收的技术

这是一系列介绍编写两个 Rust crate 的不同设计决策的文章中的第一篇,两个 crate 分别是:

  • gc-arena:用于安全地为 Rust 配备增量垃圾收集的系统,被 Ruffle 使用。
  • piccolo:用安全的 Rust 编写的无栈 Lua 运行时,专注于安全沙盒。

Rust 和垃圾收集
Rust 目前没有“真正的”垃圾收集(带循环检测),通常将RcArc视为一种无循环检测的垃圾收集形式。gc-arena是将带有这种垃圾收集指针的 Rust 作为库进行改造的尝试。piccolo需要一个垃圾收集器,且要求垃圾收集指针为零成本普通指针,运行时大部分需内存安全,现有设计无法满足这些要求,于是gc-arena诞生,其采用类似 PUC-Rio Lua 的增量收集器。

跟踪和内部可变性
为 Rust 设计带有安全接口的跟踪垃圾收集库具有挑战性,主要围绕正确跟踪持有管理(垃圾收集)指针的值以及确定垃圾收集器“根”的集合等问题。通过示例逐步构建双链表数据结构来探讨这些问题及gc-arena的解决方案:

  • 定义双链表节点Node,其包含垃圾收集指针Gc,通过Collect trait 确保可追溯性,unsafe实现该 trait 时要确保每个可达指针都被跟踪,可通过 proc-macro 安全实现。
  • 由于对象可能包含循环引用,Drop trait 会导致问题,gc-arena通过禁止Drop impl 来解决,内部使用MustNotImplDrop trait 实现。
  • gc-arena是增量收集器,存在新问题,如用户在增量跟踪操作中修改已跟踪对象可能导致悬空指针,gc-arena通过Collect trait 和“颜色不变量”来保护,内部可变性类型不能直接实现Collect,需使用gc-arena提供的版本并遵循写屏障契约。

使用生成性查找 GC 根
Rust 借用检查器的一个意外特性引入了“生成性”,可用于垃圾收集。gc-arena利用此特性,通过'gc生命周期标记所有垃圾收集值,控制指针生命周期。“竞技场”在系统编程中用于分配具有相同生命周期的内存,gc-arena中的“竞技场”也有类似含义,通过Gc指针和PhantomData类型控制指针,Arena类型参数化于根类型R,通过Arena::mutate方法控制对竞技场的访问,利用 Higher Rank Trait Bounds(HRTBs)确保回调在任何'gc选择下都有效,从而证明安全垃圾收集的属性。

生命周期投影
定义Arena本身是最后一个难题,需要一种能在不同'gc生命周期间进行替换的类型,类似 GAT。gc-arena未使用 GAT 定义根类型,而是使用带有常规生命周期参数的 trait 和 trait 绑定,通过Rootable!宏滥用 trait 对象来实现,无需为每个新的Arena类型声明新类型,每个Rootable!调用都指向相同类型。

最后给出一个双链表的完整工作示例,展示gc-arena的各个部分如何协同工作。

总结:gc-arena是一个为 Rust 配备增量垃圾收集的库,通过一系列技术解决了垃圾收集中的各种问题,但其使用具有较高的认知和可用性开销,未来将进一步讨论其改进方向。

阅读 17
0 条评论