#垃圾回收 #垃圾 #内存 #插件

jrsonnet-gc

针对Rust的跟踪垃圾回收插件

2个版本

0.4.2 2021年7月4日
0.4.1 2021年7月4日

#411 in 内存管理

Download history 325/week @ 2024-04-01 358/week @ 2024-04-08 724/week @ 2024-04-15 465/week @ 2024-04-22 306/week @ 2024-04-29 198/week @ 2024-05-06 644/week @ 2024-05-13 305/week @ 2024-05-20 213/week @ 2024-05-27 923/week @ 2024-06-03 255/week @ 2024-06-10 242/week @ 2024-06-17 278/week @ 2024-06-24 176/week @ 2024-07-01 182/week @ 2024-07-08 284/week @ 2024-07-15

928 次每月下载
5 个Crate中(4个直接使用) 使用

MPL-2.0 许可证

48KB
1K SLoC

jrsonnet-gc

Manishearth/rust-gc 分支出来,用于jrsonnet特定的修改,这些修改在上游无法接受

为Rust设计的简单跟踪(标记和清除)垃圾回收器

设计和动机在这篇博客文章中说明,代码的草图在这个gist中。

关于cgc的初始设计及其实验性并发分支的另一篇帖子

如何使用

要将它包含到您的项目中,请将以下内容添加到您的Cargo.toml中

[dependencies]
gc = { version = "0.4", features = ["derive"] }

这可以像Rc一样使用,除了内部可变性。

尽管它可以广泛使用,但它的目的是仅在需要时使用,遵循Rust的“按需付费”模型。当RcBox同样适用时,请避免使用Gc

放在Gc中的类型必须实现TraceFinalize。最简单的方法是使用gc_derive crate

use gc::{Finalize, Gc, Trace};

#[derive(Trace, Finalize)]
struct Foo {
    x: Option<Gc<Foo>>,
    y: u8,
    // ...
}

// now, `Gc<Foo>` may be used

Finalize也可以直接在结构体上实现,以添加自定义的清理行为

use gc::{Finalize, Trace};

#[derive(Trace)]
struct Foo {...}

impl Finalize for Foo {
    fn finalize(&self) {
        // Clean up resources for Foo, because we think it will be destroyed.
        // Foo may not be destroyed after a call to finalize, as another
        // finalizer may create a reference to it due to reference cycles.
    }
}

对于在stdlib中定义的类型,请在此仓库中提交一个问题(使用下面的unsafe_ignore_trace方法来使事情在同时工作)。

请注意,Trace 仅适用于包含 Gc 的类型,如果确定不是这种情况,可以在您的类型上使用 unsafe_empty_trace! 宏。或者,在结构体字段上使用 #[unsafe_ignore_trace] 注解。不正确使用 unsafe_empty_traceunsafe_ignore_trace 可能会导致不安全。

use gc::{Finalize, Gc, Trace};
use bar::Baz;

#[derive(Trace, Finalize)]
struct Foo {
    x: Option<Gc<Foo>>,
    #[unsafe_ignore_trace]
    y: Baz, // we are assuming that `Baz` doesn't contain any `Gc` objects
    // ...
}

要使用 Gc,只需调用 Gc::new

let x = Gc::new(1_u8);
let y = Gc::new(Box::new(Gc::new(1_u8)));

#[derive(Trace, Finalize)]
struct Foo {
    a: Gc<u8>,
    b: u8
}

let z = Gc::new(Foo {a: x.clone(), b: 1})

Gc 上调用 clone() 将创建另一个指向同一对象的垃圾收集引用。在可能的情况下,尽量使用内部值的借用引用而不是克隆 Gc -- Gc 实现 Deref 且与借用兼容。

Gc 是一个不可变容器。与 Rc 类似,为了获得可变性,我们必须使用单元格类型。stdlib 中的常规 RefCell 不适用于 Gc(因为它没有实现 Trace),而是使用 GcCellGcCell 的行为与 RefCell 非常相似,但它在内部帮助跟踪 GC 根。

#[derive(Trace, Finalize)]
struct Foo {
    cyclic: GcCell<Option<Gc<Foo>>>,
    data: u8,
}

let foo1 = Gc::new(Foo {cyclic: GcCell::new(None), data: 1});
let foo2 = Gc::new(Foo {cyclic: GcCell::new(Some(foo1.clone())), data: 2});
let foo3 = Gc::new(Foo {cyclic: GcCell::new(Some(foo2.clone())), data: 3});
*foo1.cyclic.borrow_mut() = Some(foo3.clone());

已知问题

  • 析构函数不应访问 Gc/GcCell 值。这通过 Trace 自定义 derive 自动实现 Drop 来强制执行,其中包含一个安全空释放方法。应使用 Finalize 来进行清理。
  • 需要为跨 crate derive 提供更好的说明。
  • 当前的 GC 不是并行的,并且被 GC 的对象局限于一个线程。在 此拉取请求 中有一个实验性的并发收集器。
  • RuScript:使用单线程 rust-gc 为各种对象分配内存

依赖项

~240KB