Vec的內存佈局
Vec所存儲的數據部分在堆內存中,同時在棧空間中存放了該vec的胖指針。胖指針包括三部分元數據:
- 指向堆的指針(一個機器字長)
- 當前vec元素數量(即長度,usize,一個機器字長)
- vec的容量(即當前vec最多可存放多少元素,usize,一個機器字長)
因此,vec的內存佈局大致如下:
vec擴容:重新分配內存
當向vec插入新元素時,如果沒有空閒容量,則會重新申請一塊內存,大小為原來vec內存大小的兩倍(官方手冊指明目前Rust並沒有確定擴容的策略,以後可能會改變),然後將原vec中的元素拷貝到新內存位置處,同時更新vec的胖指針中的元數據。
例如,有一個容量為10、長度為0的空vec,向該vec中插入前10個元素時不會重新分配內存,但在插入第11個元素時,因容量不夠,會重新申請一塊內存,容量為20,然後將前10個元素拷貝到新內存位置並將第11個元素放入其中。
通過vec的len()
方法可獲取該vec當前的元素數量,capacity()
方法可獲取該vec當前的容量大小。
fn main(){ let mut v1 = vec![11,22,33]; // len: 3, cap: 3 println!("len: {}, cap: {}", v1.len(), v1.capacity()); // push()向vec中插入一個元素,將導致擴容, // 擴容將導致重新分配vec的內存 v1.push(44); // len: 4, cap: 6 println!("len: {}, cap: {}", v1.len(), v1.capacity()); }
顯然,當頻繁擴容或者當元素數量較多且需要擴容時,大量的內存拷貝會降低程序的性能。
因此,如果可以的話,可以採取如下方式:
- 在創建vec的時候使用
Vec::with_capacity()
指定一個足夠大的容量值,以此來儘量減少可能的內存拷貝。 - 通過
reserve()
方法來調整已存在的vec容量,使之至少有指定的空閒容量數,以此來儘量減少可能的內存拷貝。
例如:
fn main(){ // 創建一個容量為3的空vec let mut v1 = Vec::with_capacity(3); v1.push(11); v1.push(22); v1.push(33); // len: 3, cap: 3 println!("len: {}, cap: {}", v1.len(), v1.capacity()); // 調整v1,使其至少要有10個空閒位置 v1.reserve(10); // len: 3, cap: 13 println!("len: {}, cap: {}", v1.len(), v1.capacity()); // 當空閒容量足夠時,reserve()什麼也不做 v1.reserve(5); println!("len: {}, cap: {}", v1.len(), v1.capacity()); }
另外,可以使用shrink_to_fit()
方法來釋放剩餘的容量。一般情況下,不會主動去釋放容量。