ChatGPT解决这个技术问题 Extra ChatGPT

Rust 有什么而不是垃圾收集器?

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

所以在这个例子中,我知道 Rust 在超出范围时回收分配给 a 的内存。

{
    let a = 4
}

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

“确定性对象生命周期”。类似于 C++。
@user2864740 该指南已经过时了。现代替代品可能是 doc.rust-lang.org/book/references-and-borrowing.html
Rust 垃圾收集,就像任何其他实用的编程语言一样。就是那个everybody thinks about garbage collection the wrong way

A
Ayonix

垃圾收集通常定期或按需使用,例如堆接近满或高于某个阈值。然后它会根据 algorithm 查找未使用的变量并释放它们的内存。

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

Rust 也允许某种垃圾收集,例如 atomic reference counting


通过在引入变量时分配内存并在不再需要内存时释放内存?我真的不知道你想说什么。也许我们对什么是 GC 有不同的看法。
他的问题是 Rust 的方法与典型的 GC 有何不同。所以我解释了 GC 是什么以及 Rust 如何在没有 GC 的情况下做到这一点。
doc.rust-lang.org/book/the-stack-and-the-heap.html 很好地解释了它。是的,很多东西都在堆栈中,但更不用说是不够的指标(见方框)。为了简单起见,我把它省略了,因为这个问题是普遍问的
@Amomum 实际上,Rust 没有任何像 C 一样的受膏 new() 函数,它们只是静态函数,特别是像 let x = MyStruct::new() 这样的东西在堆栈上创建它的对象。堆分配的 real 指标是 Box::new()(或任何依赖于 Box 的结构)。
还有哪些其他语言以与 Rust 类似的方式处理内存管理?
M
Matthias Braun

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

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

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 本身关联的内存缓冲区

因此,由于所有权跟踪,所有程序对象的生命周期都与一个(或多个)函数变量严格绑定,最终将超出范围(当它们所属的块结束时)。

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


“带有垃圾收集器的语言会定期扫描内存(一种或另一种方式)”。许多人这样做,但总的来说并非如此。实时垃圾收集器以增量方式而不是定期进行扫描。像 Mathematica 这样的引用计数语言根本不扫描。
“我不认为引用计数是一个完整的垃圾收集机制,因为它必须被补充以避免泄漏循环”。 RC 通常被认为是 GC 的一种形式。例如,在 Mathematica 和 Erlang 中,循环不能通过设计创建,因此 RC 不会泄漏。有关高级观点,请参阅“垃圾收集的统一理论”cs.virginia.edu/~cs415/reading/bacon-garbage.pdf
“我看不出周期性不包括增量情况”。例如,停止世界算法将被视为周期性,而三色标记被视为增量。在这种情况下,它们是对立的。
@JD你走得太深了。他的解释与 GC 的工作原理无关,只与 GC 和非 GC 语言之间的差异有关。您试图做出的区分是基于 GC 本身的实现。他试图做出的区分是抽象的 GC 之间的区别。在这种情况下,没有必要深入研究“周期性”的语义含义 500 个单词。
抽象地说,我们通常将使用 RAII/RC 的 C++/Rust 等语言视为非垃圾收集。以及诸如 Java/Python/C# 之类的语言将进行垃圾收集(即使它使用 RC 作为底层实现)。核心区别在于,在 C++/Rust 中,RC 是显式的,它实际上是一个 5 行包装器,围绕调用 malloc 和释放自己。在 GC 语言中,它是从视图中抽象出来的,并且类是通过引用而不是值传递的。 (而且语言规范很少提到它的 RC 还是 Mark-and-sweep,这通常是一个实现细节)
S
Swiss

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

从实际的角度来看,这种无意的内存清理被用作自动内存存储的一种方式,它将在函数范围结束时被清除。

此处提供了更多信息:https://doc.rust-lang.org/book/the-stack-and-the-heap.html


虽然使用堆栈很方便,但如果所有值都“在堆上创建”,则仍然可以处理确定性对象的生命周期。因此它是一个实现细节;不一定是语言策略。
你一直用那个词。我不认为这意味着你认为它意味着什么。
表示what I wish to express;与不确定的生命周期相反。提供更好的短语。
谢谢你的回答,我给第一个点只是因为它是第一个提交的。这些信息同样有用且有效。
@user2864740 确定性对象生命周期是指能够准确判断对象的内存何时会在其析构函数被调用后被清除。它与首先如何调用该析构函数无关。即使它对问题没有直接意义,你也会不断地重复提出同一个术语。
Y
Yilmaz

有些语言有引用计数,有些有垃圾收集器。 Rust 避免了两者,相反,如果你想在任何时间点拥有一个内存位置,它只允许一个变量名或别名。您可以将所有权从一个变量名移动到另一个变量名,但不能有两个变量名指向同一个内存地址(共享所有权除外。Rust 提供了引用计数指针类型 Rc 和 Arc。因为有时很难找到具有您需要的生命周期的单个所有者的每个值)。

Rust 使用了一种相对独特的内存管理方法,它结合了内存“所有权”的概念。基本上,Rust 会跟踪谁可以读写内存。它知道程序何时使用内存,并在不再需要时立即释放内存。它在编译时强制执行内存规则,几乎不可能出现运行时内存错误。您不需要手动跟踪内存。编译器会处理它。

Discord 最近在其一项服务中从 Go 切换到 Rust,只是因为垃圾收集器导致延迟。他们很好地解释了他们这样做的原因,您将了解有关垃圾收集器和 rust 内存系统的更多信息:

https://discord.com/blog/why-discord-is-switching-from-go-to-rust#:~:text=Discord%20is%20a%20product%20focused,and%20messages%20you%20have%20read


“你不能有两个变量名指向同一个内存地址” - 我很确定这正是 Rc/Arc 所做的。
Rc 和 Arc 在某些限制下允许值有多个所有者。
我看不出这如何使我之前的评论无效。无论哪种方式,您的评论都与您的陈述相冲突:“每个价值都有一个所有者”。
@IInspectable 我更新了答案。这个会比较好吗?