Rust中的内存泄露,循环引用

原创
2021/05/31 21:53
阅读数 218

Rust是一门内存安全的语言,只要不使用unsafe, 我们可以放心大胆的像使用具有GC的语言一样,无需自己管理内存。

而内存泄露,却不属于内存安全问问题,而是属于编程逻辑问题,因而编译器无法判断内存泄露。

循环引用,在GC语言中不是问题,GC会自己管理。在rust中,如果引入了内部可变性如Cell, RefCell, Rc等,如果不小心就会引入循环引用而造成内存泄露。

以下例子演示了一个树节点Node, 每个节点有孩子节点和父节点,我们创建了2个节点,他们之间互为父子孩子节点,从而使2个节点收尾相连,成为循环引用。

fn main() {
    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(None),
        children: RefCell::new(vec![]),
    });

    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Some(branch.clone())),
        children: RefCell::new(vec![]),
    });
    (*branch).children.borrow_mut().push(leaf.clone());

}
#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Option<Rc<Node>>>,
    children: RefCell<Vec<Rc<Node>>>,
}
impl Drop for Node{
    fn drop(&mut self) {
        println!("node droped");
    }
}

运行代码可以发现drop函数未被调用,原因在于两个节点使用了Rc, 由于他们之间相互引用,导致引用数量始终不能降为0,故node无法回收。

这个问他如何解决呢,答案是使用Weak来替代Rc, Weak的引用计数即使不是0,也会被回收。从代码角度看,孩子节点依托于父节点,父节点没了,孩子节点将不能独立存在。这样我们将parent节点由Rc改为Weak,由此解决循环引用问题。

fn main() {
    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Rc::downgrade(&branch.clone())),
        children: RefCell::new(vec![]),
    });
    (*branch).children.borrow_mut().push(leaf.clone());

}
#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}
impl Drop for Node{
    fn drop(&mut self) {
        println!("node droped");
    }
}

执行代码可以发现node的drop函数调用了2次,内存泄露问题不存在了。

所以我们看到,Rust在引入RefCell, RC时,使用不当会引入内存泄漏问题的,切记,切记。

参考

https://doc.rust-lang.org/book/ch15-06-reference-cycles.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部