用了这么多年Rust终于搞明白了内存分布!
整型,浮点型,struct,vec!,enum
本文是对 Rust内存布局 的学习与记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| struct A { a: i64, b: u64, }
struct B { a: i32, b: u64, }
struct C { a: i64, b: u64, c: i32, }
struct D { a: i32, b: u64, c: i32, d: u64, }
fn main() { println!("i32类型占的内存空间为:{}字节", std::mem::size_of::<i32>()); println!("i64类型占的内存空间为:{}字节", std::mem::size_of::<i64>()); println!( "[i64;4]占的内存空间为:{}字节", std::mem::size_of::<[i64; 4]>() );
println!("结构体A占的内存空间为:{}字节", std::mem::size_of::<A>()); println!("结构体B占的内存空间为:{}字节", std::mem::size_of::<B>()); println!("结构体C占的内存空间为:{}字节", std::mem::size_of::<C>()); println!("结构体D占的内存空间为:{}字节", std::mem::size_of::<D>()); }
|
输出
1 2 3 4 5 6 7
| i32类型占的内存空间为:4字节 i64类型占的内存空间为:8字节 [i64;4]占的内存空间为:32字节 结构体A占的内存空间为:16字节 结构体B占的内存空间为:16字节 结构体C占的内存空间为:24字节 结构体D占的内存空间为:24字节
|
没啥好说的,和Go一样,struct会存在内存对齐/内存填充(8字节对齐)
D是因为编译器会优化内存布局,字段顺序重排
Rust中的Vec!和Go中的slice差不多,都是占24Byte,三个字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct SimpleVec<T> { len: usize, capacity: usize, data: *mut T, }
fn main() { println!( "Vec!类型占的内存空间为:{}字节", std::mem::size_of::<SimpleVec<i32>>() );
println!( "Option<i64>类型占的内存空间为:{}字节", std::mem::size_of::<Option<i64>>() ); }
|
1 2
| Vec!类型占的内存空间为:24字节 Option<i64>类型占的内存空间为:16字节
|
但是对于enum类型,
会有一个tag字段,uint64,来标记变体,是None值还是Some值
1 2 3 4
| struct Option { uint64 tag; i64; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct SimpleVec<T> { len: usize, capacity: usize, data: *mut T, }
enum Data { I32(i32), F64(f64), Bytes(SimpleVec<u8>), }
fn main() { println!( "Data这个Enum类型占的内存空间为:{}字节", std::mem::size_of::<Data>() ); }
|
输出为:
Data这个Enum类型占的内存空间为:32字节
Rust的enum类似C++ std::variant的实现(大致是用union实现的)
union的内存大小是其成员中最大的那个成员的大小,
类似的,对于Data这个Enum类型,会选择最大的那个成员的大小
所以24+tag的8字节,最终为32字节
(tag在这里就用来标识其为i32,还是f64,或者是Vec)
嵌套的枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| struct SimpleVec<T> { len: usize, capacity: usize, data: *mut T, }
enum Data { I32(i32), F64(f64), Bytes(SimpleVec<u8>), }
type OptData = Option<Data>;
fn main() { println!( "OptData这个Option类型占的内存空间为:{}字节", std::mem::size_of::<OptData>() ); }
|
输出:
OptData这个Option类型占的内存空间为:32字节
因为编译器会对嵌套的枚举类型进行优化,会将其tag展开, 把多个tag合在一起了,类似下面:
展开变成一个枚举(None是uint64,能标识非常多信息)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type OptData = Option<Data>;
enum Option { Some, None, }
enum OptData_ { I32(i32); F64(f64); Bytes(SimpleVec<u8>), None }
|
元组tuple
rust中的元组大小固定吗?
在Rust中,元组的大小是固定的。这里解释一下元组大小固定的含义:
元组中的元素类型和数量在编译期就已经确定,不能在运行期修改。
编译器会根据元组中元素的类型,预先分配足够的内存用于存储这些元素。
元组的内存布局和大小也在编译期就确定下来了,运行期不会改变。
尝试创建包含不同类型或数量元素的元组,是编译时错误。
举个例子:
1
| let tuple = (1, 2.0, "three");
|
这里元组包含一个i32,一个f64和一个字符串。编译器会预先知道:
- 元组元素类型为i32, f64, &str
- i32占用4字节,f64占用8字节,&str占据一个指针的空间
- 所以该元组占用的内存大小为4 + 8 + 8 = 20字节
这20字节的内存在编译时就已分配,运行期不会改变。
如果后续试图给这个元组添加或减少元素,编译都会报错。
所以说,元组的大小和内容是固定的,这是Rust实现方式的一部分。
更多可参考
Rust 数据内存布局
原文链接: https://dashen.tech/2020/09/22/Rust内存管理/
版权声明: 转载请注明出处.