无论采用何种策略,在程序中管理资源(包括内存)的基本理念都是可以回收与无法访问的“对象”相关的资源。除了内存,这些资源还可以是互斥锁、文件句柄、套接字、数据库连接......
带有垃圾收集器的语言会定期扫描内存(一种或另一种方式)以查找未使用的对象,释放与它们相关的资源,最后释放这些对象使用的内存。
Rust 没有 GC,它是如何管理的?
Rust 拥有所有权。使用affine type system,它会跟踪哪个变量仍然持有对象,并且当这样的变量超出范围时,调用它的析构函数。您可以很容易地看到仿射类型系统的效果:
fn main() {
let s: String = "Hello, World!".into();
let t = s;
println!("{}", s);
}
产量:
<anon>:4:24: 4:25 error: use of moved value: `s` [E0382]
<anon>:4 println!("{}", s);
<anon>:3:13: 3:14 note: `s` moved here because it has type `collections::string::String`, which is moved by default
<anon>:3 let t = s;
^
这完美地说明了在任何时间点,在语言级别,所有权都会被跟踪。
这种所有权是递归的:如果你有一个Vec<String>(即一个动态的字符串数组),那么每个String都归Vec所有,而Vec本身又归一个变量或另一个对象所有,等等。 .. 因此,当一个变量超出范围时,它会递归地释放它持有的所有资源,甚至是间接的。对于Vec<String>,这意味着:
- 释放与每个
String关联的内存缓冲区
- 释放与
Vec 本身关联的内存缓冲区
因此,由于所有权跟踪,所有程序对象的生命周期都与一个(或多个)函数变量严格绑定,最终将超出范围(当它们所属的块结束时)。
注意:这有点乐观,使用引用计数(Rc 或 Arc)可能会形成引用循环,从而导致内存泄漏,在这种情况下,与循环相关的资源可能永远不会被释放。