5 分钟速读之 Rust 权威指南(二十九)循环引用
循环引用
对于前端同学来说,了解 JS 垃圾回收机制的话,一定也听过循环引用的概念,两个数据相互引用,想成一个环,由于环中每一个指针的引用计数都不可能减少到 0,所以对应的值也不会被释放丢弃,这就造成了内存泄漏,前面我们学到的 Rc<T>类型,同样存在这种问题。
制造循环引用
这里仍然使用前面的例子来试图制造两个相互引用的链表:
下面在变量 b 中创建了一个 Rc<List>实例来存放初值为 10 和指向列表 a 的 Rc<List>:
最后,把 a 的第二项指向 b,造成循环引用:
可以看到将 a 修改为指向 b 之后,a 和 b 中都有的 Rc<List> 实例的引用计数为 2。在 main 的结尾,rust 会尝试首先丢弃 b,这会使 a 和 b 中 Rc<List> 实例的引用计数减 1。然而,因为 a 仍然引用 b 中的 Rc<List>,Rc<List> 的引用计数是 1 而不是 0,由于其内存的引用计数为 1,所以 Rc<List> 在堆上的内存不会被丢弃,将会永久保留。
避免引用循环:将 Rc<T> 变为 Weak<T>
我们可以使用弱引用类型 Weak<T>来防止循环引用:
使用弱引用连接枝干和叶子节点:
使用 Rc::downgrade 时会得到 Weak<T> 类型的智能指针,每次调用 Rc::downgrade 会将 weak_count 加 1,用于记录有多少个弱引用,而实例被清理时,关注的是 strong_count,只要变成 0 就会清理,而不关心弱引用 weak_count 的数量。
观察 strong_count 和 weak_count 的改变
下面我们使用 Rc::strong_count() 和 Rc::weak_count() 方法来观察一下强引用和弱引用的区别,注意他们在作用域销毁时的表现:
所以当我们的数据类型有循环引用关系的时候便可以使用 Weak<T>类型,使相互引用的数据在指向彼此的同时避免产生循环引用和内存泄漏。
版权声明: 本文为 InfoQ 作者【码生笔谈】的原创文章。
原文链接:【http://xie.infoq.cn/article/7e5bb27dfe24a44c56b2e2fdd】。文章转载请联系作者。
评论