Rust系列(二) 内存管理

本文探讨了Rust编程语言中的内存管理机制,包括所有权原则、Move和Copy语义、可变与不可变借用以及引用计数。Rust通过绑定堆内存的生命周期于栈内存来简化管理,同时利用结构体对齐和枚举设计优化存储效率。此外,还提到了Vec和String的内存布局,它们在内存中包含指针、容量和长度信息。
摘要由CSDN通过智能技术生成

上一篇:Rust系列(一) 所有权和生命周期

通过前面的文章,目前我已经了解到了单一所有权、Move语义、Copy语义、可变和不可变借用以及引用计数。突然回首可以发现,Move 语义和 Copy 语义保证了值的单一所有权;而可变和不可变借用又可以避免对象在作为函数参数进行传递时造成额外的内存开销;引用计数提供了一种突破值的单一所有权限制的手段,得以实现多线程操作同一块内存和实现DAG等操作,使得 rust 和其他语言一样的灵活。

而上述的这些场景我们可以发现最终都是为了合理的管理内存,它希望在生命周期内就可以确定值内存的释放时机,而不必像Java和Go一样需要引入垃圾回收,也不必需要像C一样需要手动维护内存的申请和释放。
显然,对于栈变量,其内存管理和维护与函数的堆栈密切相关,我们无需特别关心,其在编译期间就已经确定了创建、存放和销毁的时机地点。而对于堆上创建的变量,由于其容量大小和生命周期都是动态的,所以管理和维护相对较为复杂。

➿如何管理堆内存?

rust 的创造者们,重新审视了堆内存的生命周期,发现大部分堆内存的需求在于动态大小,小部分需求是更长的生命周期。所以它默认将堆内存的生命周期和使用它的栈内存的生命周期绑在一起,并留了个小口子 leaked 机制(想起了上一篇文章里面的Rc::new方法),让堆内存在需要的时候,可以有超出栈帧存活的生命周期。
enum

1. 结构体内存对齐

C语言的对齐规则如下,而 rust在编译器层面进行了优化:
● 首先确定每个域的长度和对齐长度,原始类型的对齐长度和类型的长度一致。
● 每个域的起始位置要和其对齐长度对齐,如果无法对齐,则添加 padding 直至对齐。
● 结构体的对齐大小和其最大域的对齐大小相同,而结构体的长度则四舍五入到其对齐的倍数。

use std::mem::{align_of, size_of};

struct S1 {
    a: u8,
    b: u16,
    c: u8,
}

struct S2 {
    a: u8,
    c: u8,
    b: u16,
}

fn main() {
    // sizeof S1: 4, S2: 4
    println!("sizeof S1: {}, S2: {}", size_of::<S1>(), size_of::<S2>());
    // alignof S1: 2, S2: 2
    println!("alignof S1: {}, S2: {}", align_of::<S1>(), align_of::<S2>());
}

2. enum

Rust 下 enum 是一个标签联合体(tagged union),它的大小是标签的大小+最大类型的长度。通过 size_of 方法可以很方便的实验出不同平台下,不同类型 enum 的大小:

enum E1 {
    DblVal1(f64),
}

enum E2 {
    DblVal1(f64),
    DblVal2(f64),
    DblVal3(f64),
    DblVal4(f64),
}

fn main() {
    // Size is 8
    println!("Size is {}", std::mem::size_of::<E1>());
    // Size is 16
    println!("Size is {}", std::mem::size_of::<E2>());
    // Size is 24
    println!("Size is {}", std::mem::size_of::<String>());
    // Size is 24
    println!("Size is {}", std::mem::size_of::<Result<String, ()>>());
}

有关具体的使用方法请参考官方文档,和 Java 等其他语言 enum 一个比较大的区别是, Rust enum 内部的每一个枚举可以是不同的类型。此外 Rust enum 的内存设计也和其他语言有较大不同,可以参考这个链接进行学习。

3. vec<Type> 和 String

fn main() {
    // Size is 24
    println!("Size is {}", std::mem::size_of::<String>());
    // Size is 24
    println!("Size is {}", std::mem::size_of::<Vec<u8>>());
}

从上面输出可以看到 StringVec<u8> 内存大小都是 24 个字节。其实,String 结构的底层就是 Vec<u8> 数组。而 Vec<T> 类型的内存布局也和 cpp 里面的 vector 内存布局相似,包含一个指向堆内存的指针 pointer、分配的堆内存的容量 capacity,以及数据在堆内存的长度 length

📫推荐一个Rust备忘录开源项目: https://github.com/ralfbiedert/cheats.rs/

下一篇:Rust系列(三) 类型系统

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值