Rust有什么代替垃圾收集器?


95

我知道Rust没有垃圾收集器,并且想知道当绑定超出范围时如何释放内存。

因此,在此示例中,我了解到Rust超出范围时,Rust回收分配给“ a”的内存。

{
    let a = 4
}

我遇到的问题是,首先是如何发生的,其次,这不是一种垃圾收集吗?它与“典型”垃圾收集有何不同?


12
“确定性对象生存期”。与C ++类似。
user2864740 2015年

@ user2864740该指南已经过时了。现代替代品可能是doc.rust-lang.org/book/references-and-borrowing.html
Veedrac

Answers:


74

垃圾回收通常是定期或按需使用的,例如,如果堆接近满或超过某个阈值。然后,根据算法,查找未使用的变量并释放它们的内存。

Rust会知道该变量何时超出范围或其生存期在编译时结束,因此可以插入相应的LLVM /汇编指令以释放内存。

Rust还允许某种类型的垃圾收集,例如原子引用计数


通过在引入变量时分配内存并在不再需要内存时释放内存?我真的不知道你想怎么说。也许我们对当时的GC有不同的看法。
Ayonix

1
他的问题是Rust的方法与典型的GC有何不同。因此,我解释了什么是GC以及Rust在没有GC的情况下是如何做到的。
Ayonix

1
doc.rust-lang.org/book/the-stack-and-the-heap.html解释得很好。是的,很多东西都在堆栈中,但更不用说这还不足以说明问题(请参见方框)。为了简单起见,我忽略了这一点,因为这个问题总体上是在问
Ayonix

1
@Amomum实际上,Rust没有new()像C这样的受膏函数,它们只是静态函数,尤其是let x = MyStruct::new()在堆栈上创建其对象的东西。堆分配的真正指标是Box::new()(或任何依赖Box的结构)。
马里奥·卡内罗

1
还有哪些其他语言以与Rust类似的方式处理内存管理?
still_dreaming_1

43

无论采用何种策略,在程序中管理资源(包括内存)的基本思想是可以回收与无法访问的“对象”相关的资源。除了内存之外,这些资源还可以是互斥锁,文件句柄,套接字,数据库连接...

具有垃圾回收器的语言会定期(一种或另一种方式)扫描内存以查找未使用的对象,释放与它们关联的资源,最后释放那些对象使用的内存。

Rust没有GC,它如何管理?

Rust拥有所有权。使用仿射类型系统,它跟踪哪个变量仍保留在对象上,并在此类变量超出范围时调用其析构函数。您可以很容易地看到仿射类型系统生效:

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<String>

  1. 释放与每个内存关联的内存缓冲区 String
  2. 释放与Vec自身关联的内存缓冲区

因此,由于所有权跟踪,所有程序对象的生存期都严格地与一个(或几个)函数变量联系在一起,这些变量最终将超出范围(当它们属于的块结束时)。

注意:这有点乐观,使用引用计数(RcArc)可以形成引用循环,从而导致内存泄漏,在这种情况下,与循环相关的资源可能永远不会释放。


2
“带有垃圾收集器的语言会定期(一种或另一种方式)扫描内存”。许多人这样做,但总的来说是不正确的。实时垃圾收集器将进行增量扫描,而不是定期扫描。引用计数语言(如Mathematica)根本不会扫描。
JD

@JonHarrop:我不将引用计数视为完整的垃圾收集机制,因为必须对其进行补充以避免泄漏周期。至于渐进式/周期性的差异,可能是我的英语水平不佳,但我看不到周期性如何无法涵盖渐进式的情况...我认为“(一种或另一种)”位足以传达出许多变化存在方法。无论如何,如果您有一个更好地简洁描述垃圾收集的方法,请提出建议。但是,我无意对自己的观点进行全面的解释:我对此没有资格。
Matthieu M.

1
“我不将引用计数视为完整的垃圾收集机制,因为必须补充引用以避免泄漏周期”。RC通常被视为GC的一种形式。例如,在Mathematica和Erlang中,无法通过设计创建周期,因此RC不会泄漏。有关高级观点,请参见“垃圾收集的统一理论” cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
JD

@JonHarrop:是的,如果不可能循环,则RC无法泄漏。
Matthieu M.

2
“我看不到定期如何不能涵盖增量案件”。例如,停止世界算法将被视为周期性算法,而三色标记将被视为增量算法。在这种情况下,它们是相反的。
JD

6

使用必须手动管理内存的语言,堆栈和堆之间的区别就变得至关重要。每次调用函数时,堆栈中都会为该函数范围内包含的所有变量分配足够的空间。当函数返回时,与该函数关联的堆栈帧从堆栈中“弹出”,并且释放内存以供将来使用。

从实际的角度来看,这种无意中的内存清理被用作自动内存存储的一种方式,该功能将在功能范围的末尾清除。

这里有更多信息:https : //doc.rust-lang.org/book/the-stack-and-the-heap.html


3
尽管使用堆栈很方便,但是如果所有值都是在堆上创建的,则仍然可以处理确定性对象的生存期。因此,这是一个实现细节;不一定是语言策略。
user2864740

2
你一直在用那个词。我认为这并不意味着您认为的意思。
瑞士,

手段就是我想表达; 与不确定性寿命相反。提出一个更好的短语。
user2864740

感谢您的回答,我将第一点的观点仅仅因为它是首先提交的。这些信息同样有用和有效。
rix

@ user2864740确定性对象生存期指的是一旦调用了析构函数后,便能够准确分辨出何时清除对象的内存。它与该析构函数最初的调用方式无关。即使对这个问题没有直接意义,您仍会反复提出相同的术语。
瑞士时间
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.