C++之vector內(nèi)存釋放原理
C++ vector內(nèi)存釋放
C++ STL容器 vector 的工作原理
vector容器的元素以連續(xù)方式存放,每一個元素都緊挨著前一個元素存儲,類似數(shù)組的內(nèi)存結(jié)構(gòu)。
系統(tǒng)預(yù)先給vector容器分配一塊 capactity 大小的內(nèi)存空間,當(dāng)插入的數(shù)據(jù)超過這個空間的時候,這塊空間會讓某種方式擴展,即通過一定倍數(shù)大小重新分配空間、拷貝元素、撤銷舊空間,在vs下為1.5倍擴容,在linux下為2倍擴容。但是為了防止大量分配連續(xù)內(nèi)存的開銷,在使用clear和erase刪除數(shù)據(jù)的時候,它只做了數(shù)據(jù)清除工作,沒有釋放內(nèi)存,所以vector容器的內(nèi)存是不會縮小的。
也就是說,vector容器實現(xiàn)內(nèi)存自增長,在vector對象不釋放的情況下內(nèi)存只會增長而不會釋放,只有當(dāng)vector對象銷毀才會釋放這個內(nèi)存。
如下示例,打印使用clear和erase刪除數(shù)據(jù)前后vector對象的數(shù)據(jù)容量和內(nèi)存容量大小
#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
vector<int> vt;
for (int i = 0; i < 10; i++)
{
vt.push_back(i);
}
cout << "size:" << vt.size() << endl;
cout << "capacity:" << vt.capacity() << endl;
#if 0
vt.clear();
#else
for (auto item = vt.begin(); item != vt.end(); )
{
item = vt.erase(item);
}
#endif
cout << "size:" << vt.size() << endl;
cout << "capacity:" << vt.capacity() << endl;
system("pause");
return 0;
}使用clear和erase刪除數(shù)據(jù)測試結(jié)果入下:

從示例不難看出使用clear和erase無法釋放vector容器內(nèi)存,那對于vector容器,我們應(yīng)該如何釋放呢?
最簡單的就是使用 swap()交換函數(shù),使vector離開其自身的作用域,從而強制釋放vector所占的內(nèi)存空間。
template < class T >
void ClearVector( vector< T >& vt )
{
vector< T > vtTemp;
veTemp.swap( vt );
}一般的STL內(nèi)存管理器allocator都是用內(nèi)存池來管理內(nèi)存的,所以某個容器申請內(nèi)存或釋放內(nèi)存都只是影響到內(nèi)存池的剩余內(nèi)存量,而不是真的把內(nèi)存歸還給系統(tǒng)。
這樣做一是為了避免內(nèi)存碎片,二是提高了內(nèi)存申請和釋放的效率。
事實上,vector容器在內(nèi)存不夠時就向內(nèi)存管理框架申請內(nèi)存,內(nèi)存管理框架如果發(fā)現(xiàn)內(nèi)存不夠了,就malloc內(nèi)存,但是當(dāng)vector釋放資源的時候(比如destruct), stl根本就不調(diào)用free以減少內(nèi)存,因為內(nèi)存分配在stl的底層:stl假定如果你需要更多的資源就代表你以后也可能需要這么多資源(你的list, hashmap也是用這些內(nèi)存),所以就沒必要不停地malloc/free。
注意:這邊說到的是系統(tǒng)分配的內(nèi)存的申請/釋放。如果vector中存放的是指針,那么當(dāng)vector銷毀時,這些指針指向的對象不會被銷毀,那么內(nèi)存就不會被釋放。開發(fā)者自己申請的內(nèi)存記得自己釋放。
C++容器vector內(nèi)存釋放問題
突然遇到vector在使用clear之后,其內(nèi)存(capacity不變)無法釋放的問題:
? ? vector<int> v1{1,2,3};
? ? cout << v1.empty() << endl;
? ? cout << v1[0] << " " << v1.size() << " " << v1.capacity() << endl;
? ? v1.clear();
? ? cout << v1.empty() << endl;
? ? cout << v1[0] << " " << v1.size() << " " << v1.capacity() << endl;?輸出結(jié)果:
0
1 3 3
1
1 0 3
0 3
可以發(fā)現(xiàn),在使用clear之后,size和empty改變了,但是capacity依舊是那么大(總算是理解C++Primer:vector在生命周期內(nèi),內(nèi)存大小是只增不減)。
這里提供兩種釋放方法
第一種:
作用域+臨時變量+swap函數(shù):swap函數(shù)可以直接交換容器的內(nèi)存
? ? {
? ? ? ? std::vector<int>().swap(v1); ?
? ? }
? ? cout << v1.size() << " " << v1.capacity() << endl;運行結(jié)果:
0 0
第二種:
不易發(fā)現(xiàn)的成員函數(shù):shrink_to_fit去釋放。
? ? v1.shrink_to_fit(); ? ? cout << v1.size() << " " << v1.capacity() << endl;?
運行結(jié)果:
0 0
但是,雖然clear之后vector的內(nèi)存不會釋放,但是重新emplace_back之后的數(shù)據(jù),會從容器內(nèi)存起始地址開始重新存放。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
C語言數(shù)據(jù)結(jié)構(gòu)系列篇二叉樹的遍歷
本章將會詳細講解二叉樹遍歷的四種方式,分別為前序遍歷、中序遍歷、后續(xù)遍歷和層序遍歷。在學(xué)習(xí)遍歷之前,會先帶大家回顧一下二叉樹的基本概念2022-02-02

