一篇學會 Rust 內(nèi)存布局
圖片
題圖忘了來自哪里..
整型,浮點型,struct,vec!,enum
本文是對 Rust內(nèi)存布局[1] 的學習與記錄
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類型占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<i32>());
println!("i64類型占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<i64>());
println!(
"[i64;4]占的內(nèi)存空間為:{}字節(jié)",
std::mem::size_of::<[i64; 4]>()
);
println!("結(jié)構(gòu)體A占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<A>());
println!("結(jié)構(gòu)體B占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<B>());
println!("結(jié)構(gòu)體C占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<C>());
println!("結(jié)構(gòu)體D占的內(nèi)存空間為:{}字節(jié)", std::mem::size_of::<D>());
}
輸出
i32類型占的內(nèi)存空間為:4字節(jié)
i64類型占的內(nèi)存空間為:8字節(jié)
[i64;4]占的內(nèi)存空間為:32字節(jié)
結(jié)構(gòu)體A占的內(nèi)存空間為:16字節(jié)
結(jié)構(gòu)體B占的內(nèi)存空間為:16字節(jié)
結(jié)構(gòu)體C占的內(nèi)存空間為:24字節(jié)
結(jié)構(gòu)體D占的內(nèi)存空間為:24字節(jié)
沒啥好說的,和Go一樣,struct會存在內(nèi)存對齊/內(nèi)存填充(8字節(jié)對齊)
D是因為編譯器會優(yōu)化內(nèi)存布局,字段順序重排
Rust中的Vec!和Go中的slice差不多,都是占24Byte,三個字段
struct SimpleVec<T> {
len: usize, // 8
capacity: usize, //8
data: *mut T, //8
}
fn main() {
println!(
"Vec!類型占的內(nèi)存空間為:{}字節(jié)",
std::mem::size_of::<SimpleVec<i32>>()
);
println!(
"Option<i64>類型占的內(nèi)存空間為:{}字節(jié)",
std::mem::size_of::<Option<i64>>()
);
}
Vec!類型占的內(nèi)存空間為:24字節(jié)
Option<i64>類型占的內(nèi)存空間為:16字節(jié)
但是對于enum類型,
會有一個tag字段,uint64,來標記變體,是None值還是Some值
struct Option {
uint64 tag; // 占8字節(jié) Some None
i64; //實際存放的數(shù)據(jù)
}
struct SimpleVec<T> {
len: usize, // 8
capacity: usize, //8
data: *mut T, //8
}
enum Data {
// tag,uint64,8字節(jié)
I32(i32), // 4字節(jié),但需內(nèi)存對齊到8字節(jié)?
F64(f64), // 8字節(jié)
Bytes(SimpleVec<u8>), // 24字節(jié)
}
fn main() {
println!(
"Data這個Enum類型占的內(nèi)存空間為:{}字節(jié)",
std::mem::size_of::<Data>()
);
}
輸出為:
Data這個Enum類型占的內(nèi)存空間為:32字節(jié)
Rust的enum類似C++ std::variant的實現(xiàn)(大致是用union實現(xiàn)的)
union的內(nèi)存大小是其成員中最大的那個成員的大小,
類似的,對于Data這個Enum類型,會選擇最大的那個成員的大小
所以24+tag的8字節(jié),最終為32字節(jié) (tag在這里就用來標識其為i32,還是f64,或者是Vec)
嵌套的枚舉:
struct SimpleVec<T> {
len: usize, // 8
capacity: usize, //8
data: *mut T, //8
}
enum Data {
// tag,uint64,8字節(jié)
I32(i32), // 4字節(jié),但需內(nèi)存對齊到8字節(jié)?
F64(f64), // 8字節(jié)
Bytes(SimpleVec<u8>), // 24字節(jié)
}
type OptData = Option<Data>;
fn main() {
println!(
"OptData這個Option類型占的內(nèi)存空間為:{}字節(jié)",
std::mem::size_of::<OptData>()
);
}
輸出:
OptData這個Option類型占的內(nèi)存空間為:32字節(jié)
因為編譯器會對嵌套的枚舉類型進行優(yōu)化,會將其tag展開, 把多個tag合在一起了,類似下面:
展開變成一個枚舉(None是uint64,能標識非常多信息)
type OptData = Option<Data>;
enum Option {
Some,
None,
}
enum OptData_ {
I32(i32);
F64(f64);
Bytes(SimpleVec<u8>),
None
}
元組tuple
rust中的元組大小固定嗎?
在Rust中,元組的大小是固定的。這里解釋一下元組大小固定的含義:
- 元組中的元素類型和數(shù)量在編譯期就已經(jīng)確定,不能在運行期修改。
- 編譯器會根據(jù)元組中元素的類型,預先分配足夠的內(nèi)存用于存儲這些元素。
- 元組的內(nèi)存布局和大小也在編譯期就確定下來了,運行期不會改變。
- 嘗試創(chuàng)建包含不同類型或數(shù)量元素的元組,是編譯時錯誤。
舉個例子:
let tuple = (1, 2.0, "three");
這里元組包含一個i32,一個f64和一個字符串。編譯器會預先知道:
- 元組元素類型為i32, f64, &str
- i32占用4字節(jié),f64占用8字節(jié),&str占據(jù)一個指針的空間
- 所以該元組占用的內(nèi)存大小為4 + 8 + 8 = 20字節(jié)
這20字節(jié)的內(nèi)存在編譯時就已分配,運行期不會改變。
如果后續(xù)試圖給這個元組添加或減少元素,編譯都會報錯。
所以說,元組的大小和內(nèi)容是固定的,這是Rust實現(xiàn)方式的一部分。
更多可參考Rust 數(shù)據(jù)內(nèi)存布局[2]
參考資料
[1]Rust內(nèi)存布局: https://www.bilibili.com/video/BV1Bm4y1c71r
[2]Rust 數(shù)據(jù)內(nèi)存布局: https://blog.csdn.net/techdashen/article/details/120257323