美文网首页
Rust中的GC

Rust中的GC

作者: 躺在家里干活 | 来源:发表于2019-10-08 09:50 被阅读0次

    “GC”的新姿势 -- ownership> 编程语言中的“垃圾回收”的本质:找到并回收不再被使用的内存空间

    不要动,让我来

    JAVA语言中不需要手动去处理内存回收的问题, 虚拟机有完善的垃圾回收机制,成熟的动态内存分配和回收
    阿里妹妹推的JVM GC文章

    Talk is cheap. Show me the code.

    C语言中需要手动分配,手动释放内存空间,自己的屁股自己擦
    C语言内存处理

    Rust之ownership

    一些语言中(如JAVA)具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言(如C)中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过ownership系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,ownership(所有权)系统的任何功能都不会减慢程序。

    什么是ownership?有什么特征?(所有权规则)

    1. Rust 中的每一个值都有一个被称为其所有者(owner)的变量
    {                      
        let s = "hello"; // 这里 s 是 hello 这个值的所有者
    }       
    
    1. 值有且只有一个所有者
    fn main() {
        let s = String::from("hello");
        let a = s;
        println!("{}", s);
        println!("{}", a);
    }
    

    编译结果:

    $ rustc only_one_owner.rs
    error[E0382]: use of moved value: `s`
     --> only_one_owner.rs:4:20
      |
    3 |     let a = s;
      |         - value moved here
    4 |     println!("{}", s);
      |                    ^ value used here after move
      |
      = note: move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
    
    error: aborting due to previous error
    

    这里清楚的看到,这里编译不通过,因为当我们let a = s的时候,其实String::from("hello")的所有权转移到了变量a上,所以打印s是一个不能成功的操作。

    修复后的代码以及输出:

    fn main() {
        let s = String::from("hello");
        let a = s;
        println!("{}", a);
    }
    
    $ rustc only_one_owner.rs
    $ ./only_one_owner
    hello
    
    1. 当所有者(变量)离开作用域,这个值将被丢弃
      很显然,这里就是回收的触发点

    更多问题:

    1. 为什么使用let s = String::from("hello")不使用let s = "hello"
    String的赋值

    一个是引用,一个是值,值的重新绑定是会直接拷贝的,引用的赋值只是拷贝了指针,长度,容量(如上图)

    1. 为什么要有只能有一个owner的限制呢?
      试想如果一个值的所有者是两个变量,那么根据所有权规则第三条,当变量离开作用域的时候,就会释放两次内存(double free)。是不是想想都很可怕,鬼知道会释放了什么东西。。

    2. 我就要赋值两次,行不行?

    // 你说了算,这里就是 深浅拷贝 的问题了,应该都知道哈
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {}, s2 = {}", s1, s2);
    

    调用函数时ownership的变化

    举个🌰:

    fn main() {
        let s = String::from("hello");
        let num = 32;
        to(s, num);
        println!("main num:{}", num);
    }
    
    fn to(str: String, number: i32) {
        println!("str={},num={}",str, number);
    }
    

    编译运行:

    $ ./function_owner
    str=hello,num=32
    main num:32
    ## 可以看出,main方法中num依然是可以使用的
    

    修改这个例子:打印出变量s

    fn main() {
        ...
        println!("main num:{}, str:{}", num, s);
    }
    ...
      
      
    // 编译报错:
    error[E0382]: use of moved value: `s`
     --> function_owner.rs:5:46
      |
    4 |     to(s, num);
      |        - value moved here
    5 |         println!("main num:{}, str:{}", num, s);
      |                                              ^ value used here after move
      |
      = note: move occurs because `s` has type `std::string::String`, which does not implement the `Copy` trait
    
    error: aborting due to previous error
    

    本质上,还是上面的问题。

    函数返回结果时,ownership的变化

    应该已经很清楚了,会把被调用函数的返回值的所有权返回给调用方作用域中的变量

    fn main() {
        let s1 = String::from("hello");
        let s2 = from(s1);
        // 此时,s1已经不能操作,因为调用函数的时候,所有权已经转移了
        // s2时可以操作的,from函数的返回值的所有者就是 s2
    }
    
    fn from(str: String) {
        str
    }
    

    你这不行啊,String一旦传递给函数,就不能用了,多浪费啊

    考虑下面的代码:

    fn main() {
        let s1 = String::from("hello");
        let s2 = s1.clone(); //这一步是必须的,因为调用length()的时候,会发生所有权的转移,所有导致 s1 不再可用
        let len = length(s1);
        println!("'{}'的长度是{}", s2, len);
    }
    // 计算一个字符串的长度
    fn length(s: String) -> usize {
        s.len()
    }
    

    这里为了实现功能,我们额外clone了一份数据,是不是感觉多次一举,很不优雅,繁琐。。。

    • 如何解决传递对象(好像这样叫不太合适。。。)数据,导致所有权的转移?
    1. 记得当初学C的时候,有一个概念,指针的指针
    2. Rust上有一个概念 对象的引用&s1表示 s1的引用,并且这种引用并没有所有权,所有权还在被引用的变量(对象)中。
    3. 函数的参数可以声明成 对象的引用
    4. Show Time => (原谅我使用这俩个符号,最近在写Scala)
    fn main() {
        let s1 = String::from("hello");
        let len = length(&s1);// 这里传入的是 s1 的引用
        println!("'{}'的长度是{}", s1, len); // 这里 s1 的所有权仍在
    }
    // 计算一个字符串的长度
    fn length(s: &String) -> usize {
        s.len()
    } // s 离开作用域,由于s没有引用值的所有权,所以不会进行内存回收
    

    值,对象,对象引用关系图:


    借用

    要回收,先分类

    马云捡到一个神灯
    擦了擦灰后
    灯神出现了
    灯神:”一个愿望?”
    马云:”你说。”
    灯神:”…”
    最后灯神提出了自己的愿望:
    “把我扔了吧。” 
    马云决定帮它实现这个愿望:
    “你是什么垃圾?”
    
    
    小时候:“妈妈我是从哪里来的?”“你是从垃圾桶里捡来的。”
    长大后:“妈妈我是从哪里来的?”“你是从湿垃圾桶里捡来的。”
    
    
    垃圾分类将至,我从今日开始戒外卖,每天堂食,我将不喝咖啡,不喝奶茶,不食甜品,不吃零食,不买水果,不用护肤品。我是生活中的懒货,懒货中的佼佼者。
    

    参考引用

    Rust编程语言
    阿里妹妹推的JVM GC文章
    C语言内存处理

    我的个人博客,有空来坐坐

    相关文章

      网友评论

          本文标题:Rust中的GC

          本文链接:https://www.haomeiwen.com/subject/unlmyctx.html