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ù)超過這個空間的時候,這塊空間會讓某種方式擴(kuò)展,即通過一定倍數(shù)大小重新分配空間、拷貝元素、撤銷舊空間,在vs下為1.5倍擴(kuò)容,在linux下為2倍擴(kuò)容。但是為了防止大量分配連續(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離開其自身的作用域,從而強(qiáng)制釋放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;
運(yùn)行結(jié)果:
0 0
第二種:
不易發(fā)現(xiàn)的成員函數(shù):shrink_to_fit去釋放。
? ? v1.shrink_to_fit(); ? ? cout << v1.size() << " " << v1.capacity() << endl;?
運(yùn)行結(jié)果:
0 0
但是,雖然clear之后vector的內(nèi)存不會釋放,但是重新emplace_back之后的數(shù)據(jù),會從容器內(nèi)存起始地址開始重新存放。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++類模板與函數(shù)模板基礎(chǔ)詳細(xì)講解
C++語言的模板技術(shù)包括函數(shù)模板和類模板,模板技術(shù)是一種代碼重用技術(shù),函數(shù)和類是C++語言中兩種主要的重用代碼形式,這篇文章主要介紹了C++函數(shù)模板和類模板,需要的朋友可以參考下2022-08-08C語言數(shù)據(jù)結(jié)構(gòu)系列篇二叉樹的遍歷
本章將會詳細(xì)講解二叉樹遍歷的四種方式,分別為前序遍歷、中序遍歷、后續(xù)遍歷和層序遍歷。在學(xué)習(xí)遍歷之前,會先帶大家回顧一下二叉樹的基本概念2022-02-02C++中引用的相關(guān)知識點(diǎn)小結(jié)
引用是C++一個很重要的特性,顧名思義是某一個變量或?qū)ο蟮膭e名,對引用的操作與對其所綁定的變量或?qū)ο蟮牟僮魍耆葍r,這篇文章主要給大家總結(jié)介紹了C++中引用的相關(guān)知識點(diǎn),需要的朋友可以參考下2022-03-03