From 26e1a20eb278680b1b7b2485ae49bf3bcd73e153 Mon Sep 17 00:00:00 2001 From: Jesse <35264598+JesseAtSZ@users.noreply.github.com> Date: Sat, 29 Jan 2022 15:12:49 +0800 Subject: [PATCH] Update circle-reference.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 81行,b退出的时候触发drop,a退出的时候也会触发drop吧?所以我认为最后a、b的引用计数都是0,不会存在内存泄露 --- .../circle-self-ref/circle-reference.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/book/contents/advance/circle-self-ref/circle-reference.md b/book/contents/advance/circle-self-ref/circle-reference.md index f2b769f6..2dce3b23 100644 --- a/book/contents/advance/circle-self-ref/circle-reference.md +++ b/book/contents/advance/circle-self-ref/circle-reference.md @@ -1,8 +1,8 @@ -# Weak与循环引用 -Rust的安全性是众所周知的,但是不代表它不会内存泄漏。一个典型的例子就是同时使用`Rc`和`RefCell`创建循环引用,最终这些引用的计数都无法被归零,因此`Rc`拥有的值也不会被释放清理。 +# Weak 与循环引用 +Rust 的安全性是众所周知的,但是不代表它不会内存泄漏。一个典型的例子就是同时使用 `Rc` 和 `RefCell` 创建循环引用,最终这些引用的计数都无法被归零,因此 `Rc` 拥有的值也不会被释放清理。 ## 何为循环引用 -关于内存泄漏,如果你没有充足的Rust经验,可能都无法造出一份代码来再现它: +关于内存泄漏,如果你没有充足的 Rust 经验,可能都无法造出一份代码来再现它: ```rust use crate::List::{Cons, Nil}; use std::cell::RefCell; @@ -26,13 +26,13 @@ impl List { fn main() {} ``` -这里我们创建一个有些复杂的枚举类型`List`,这个类型很有意思,它的每个值都指向了另一个`List`,而且得益于`Rc`的使用还允许多个值指向一个`List`: +这里我们创建一个有些复杂的枚举类型 `List`,这个类型很有意思,它的每个值都指向了另一个 `List`,此外,得益于 `Rc` 的使用还允许多个值指向一个 `List`: -如上图所示,每个矩形框节点都是一个`List`类型,它们或者是拥有值且指向另一个`List`的的`Cons`,或者是一个没有值的终结点`Nil`。同时,由于`RefCell`的使用,每个`List`所指向的`List`还能够被修改。 +如上图所示,每个矩形框节点都是一个 `List` 类型,它们或者是拥有值且指向另一个 `List` 的`Cons`,或者是一个没有值的终结点 `Nil`。同时,由于 `RefCell` 的使用,每个 `List` 所指向的 `List` 还能够被修改。 -下面来使用一下这个复杂的`List`枚举: +下面来使用一下这个复杂的 `List` 枚举: ```rust fn main() { let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); @@ -62,12 +62,12 @@ fn main() { ``` 这个类型定义看着复杂,使用起来更复杂!不过排除这些因素,我们可以清晰看出: -1. 在创建了`a`后,紧接着就使用`a`创建了`b`,因此`b`引用了`a` -2. 然后我们又利用`Rc`克隆了`b`,然后通过`RefCell`的可变性,让`a`引用了`b` +1. 在创建了 `a` 后,紧接着就使用 `a` 创建了 `b`,因此 `b` 引用了 `a` +2. 然后我们又利用 `Rc` 克隆了 `b`,然后通过 `RefCell` 的可变性,让 `a` 引用了 `b` 至此我们成功创建了循环引用`a`-> `b` -> `a` -> `b` ···· -先来观察下引用计数: +先来观察下引用计数: ```console a的初始化rc计数 = 1 a指向的节点 = Some(RefCell { value: Nil }) @@ -78,9 +78,9 @@ b指向的节点 = Some(RefCell { value: Cons(5, RefCell { value: Nil }) }) 在更改a后,a的rc计数 = 2 ``` -在`main`函数结束前,`a`和`b`的引用计数均是`2`,随后`b`触发`Drop`,此时引用计数会变为`1`,并不会归`0`,因此`b`所指向内存不会被释放,同理可得`a`指向的内存也不会被释放,最终发生了内存泄漏。 +在 `main` 函数结束前,`a` 和 `b` 的引用计数均是 `2`,随后 `b` 触发 `Drop`,此时引用计数会变为 `1`,并不会归 `0`,因此 `b` 所指向内存不会被释放,同理可得 `a` 指向的内存也不会被释放,最终发生了内存泄漏。 -下面一张图很好的展示了这种引用循环关系: +下面一张图很好的展示了这种引用循环关系: 现在我们还需要轻轻的推一下,让塔米诺骨牌轰然倒塌。反注释最后一行代码,试着运行下: @@ -91,23 +91,23 @@ thread 'main' has overflowed its stack fatal runtime error: stack overflow ``` -通过`a.tail`的调用,Rust试图打印出`a -> b ->a···`的所有内容,但是在不懈的努力后,`main`线程终于不堪重负,发生了[栈溢出](https://course.rs/pitfalls/stack-overflow.html)。 +通过 `a.tail` 的调用,Rust 试图打印出 `a -> b ->a···` 的所有内容,但是在不懈的努力后,`main` 线程终于不堪重负,发生了[栈溢出](https://course.rs/pitfalls/stack-overflow.html)。 -以上的代码可能并不会造成什么大的问题,但是在一个更加复杂的程序中,类似的问题可能会造成你的程序不断的分配内存、泄漏内存,最终程序会不幸**OOM**,当然这其中的CPU损耗也不可小觑。 +以上的代码可能并不会造成什么大的问题,但是在一个更加复杂的程序中,类似的问题可能会造成你的程序不断地分配内存、泄漏内存,最终程序会不幸**OOM**,当然这其中的 CPU 损耗也不可小觑。 总之,创建引用并不简单,但是也并不是完全遇不到,当你使用`RefCell>`或者类似的类型嵌套组合(具备内部可变性和引用计数)时,就要打起万分精神,前面可能是深渊! 那么问题来了? 如果我们确实需要实现上面的功能,该怎么办?答案是使用`Weak`。 ## Weak -`Weak`非常类似于`Rc`,但是与`Rc`持有所有权不同,`Weak`不持有所有权,它仅仅保存一份指向数据的弱引用:如果你想要访问数据,需要通过`Weak`指针的`upgrade`方法实现,该方法返回一个类型为`Option>>`的值。 +`Weak` 非常类似于 `Rc`,但是与 `Rc` 持有所有权不同,`Weak` 不持有所有权,它仅仅保存一份指向数据的弱引用:如果你想要访问数据,需要通过 `Weak` 指针的 `upgrade` 方法实现,该方法返回一个类型为 `Option>>` 的值。 -看到这个返回,相信大家就懂了:何为弱引用?就是**不保证引用关系依然存在**,如果不存在,就返回一个`None`! +看到这个返回,相信大家就懂了:何为弱引用?就是**不保证引用关系依然存在**,如果不存在,就返回一个 `None`! -因为`Weak`引用不计入所有权,因此它**无法阻止所引用的内存值被释放掉**, 而且`Weak`本身不对值的存在性做任何担保,引用的值还存在就返回`Some`,不存在就返回`None`。 +因为 `Weak` 引用不计入所有权,因此它**无法阻止所引用的内存值被释放掉**,而且 `Weak` 本身不对值的存在性做任何担保,引用的值还存在就返回 `Some`,不存在就返回 `None`。 -#### Weak与Rc对比 -我们来将`Weak`与`Rc`进行以下简单对比: +#### Weak 与 Rc 对比 +我们来将 `Weak` 与 `Rc` 进行以下简单对比: | `Weak` | `Rc` | |--------|-------------|