详解STL中的空间配置器(SGI版本)
空間配置器
1.什么是空間配置器
為各個容器高效的管理空間(空間的申請與回收)的
2.為什么需要空間配置器
各種容器----->可以存放元素---->底層需要空間
new 申請空間
動態(tài)內(nèi)存管理總結
前面的容器中,每次開辟空間都用的是new,但是用new有一些不好的地方
- 空間申請與釋放需要用戶自己管理,容易造成內(nèi)存泄漏
- 頻繁向系統(tǒng)申請小塊內(nèi)存塊,容易造成內(nèi)存碎片例如:結點
- 頻繁向系統(tǒng)申請小塊內(nèi)存,影響程序運行效率
- 直接使用malloc與new進行申請,每塊空間前有額外空間浪費 (malloc會在申請的空間前后添加新的東西)
- 申請空間失敗怎么處理
- 代碼結構比較混亂,代碼復用率不高
- 未考慮線程安全問題
高效的內(nèi)存管理方式
3.SGI–STL空間配置器如何實現(xiàn)
小塊內(nèi)存
- 一級空間配置器處理大塊內(nèi)存,
- 二級空間配置器處理小塊內(nèi)存。
一級空間配置器
malloc+free----->set_new_handle
_malloc_alloc_template:
申請空間
void * allocate(size_t n) {// 申請空間成功,直接返回,失敗交由oom_malloc處理void * result =malloc(n);if(nullptr == result)result = oom_malloc(n);return result; }釋放空間
static void deallocate(void *p, size_t /* n */) { free(p); }malloc失敗后的處理oom_malloc
- 是:直接拋異常
- 申請成功:直接返回
- 申請失敗:循環(huán)繼續(xù)
set_new_handle
返回值類型以及參數(shù)類型都是void*()函數(shù)指針
// 該函數(shù)的參數(shù)為函數(shù)指針,返回值類型也為函數(shù)指針 // void (* set_malloc_handler( void (*f)() ) )() static void (* set_malloc_handler(void (*f)()))() {void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old); }二級空間配置器
頻繁向系統(tǒng)申請小的內(nèi)存塊造成的缺陷
SGI—STL采用了內(nèi)存池的技術來提高申請空間的速度以及減少額外空間的浪費,采用哈希桶的方式來提高用戶獲取空間的速度與高效管理。
內(nèi)存池
內(nèi)存池就是:先申請一塊比較大的內(nèi)存塊已做備用,當需要內(nèi)存時,直接到內(nèi)存池中去去,當池中空間不夠時,再向內(nèi)存中去取,當用戶不用時,直接還回內(nèi)存池即可。避免了頻繁向系統(tǒng)申請小塊內(nèi)存所造成的效率低、內(nèi)存碎片以及額外浪費的問題
char* _start,*finish申請空間
需要----->再對內(nèi)存塊進行分割
申請空間的缺陷
注意問題
答:優(yōu)先從鏈表中選,先從大塊中拿,如果用戶需要大塊的空間,可能就給不了了
答:不行
答:用鏈表連接起來
答:內(nèi)存碎片
二級空間配置器的設計
SGI-STL中的二級空間配置器使用了內(nèi)存池技術,但沒有采用鏈表的方式對用戶已經(jīng)歸還的空間進行管理(因為用戶申請空間時在查找合適的小塊內(nèi)存時效率比較低),而是采用了哈希桶的方式進行管理效率更高。那是否需要128桶個空間來管理用戶已經(jīng)歸還的內(nèi)存塊呢?答案是不需要,因為用戶申請的空間基本都是4的整數(shù)倍,其他大小的空間幾乎很少用到。因此:SGI-STL將用戶申請的內(nèi)存塊向上對齊到了8的整數(shù)倍
32位系統(tǒng)用4的倍數(shù)
64位系統(tǒng)用8的倍數(shù)
每個鏈表中肯定必須有一個指針,32位指針是4個字節(jié),64位下是8個字節(jié)
二級空間配置器的管理空間
void allocate(size_t n)
{
if(n>128)
; //調用一級空間配置器
1. 用n計算桶號
2. 檢測該桶中是否有結點(內(nèi)存塊)
有:使用頭刪法將第一個內(nèi)存塊返回
沒有:return refill(Round_up(n));
}
void refill(size_t/已經(jīng)是8的整數(shù)倍/)
{
size_t nobjs =20;
char* chunk = chunk_alloc(n,nobjs);
if(nobjs == 1) //只要了一塊
return chunk;
//1<nobjs<=20
將第一個內(nèi)存保存,最后要返回給外部用戶
將剩余nobjs-1個內(nèi)存塊掛接到對應的桶中
}
void * chunk_alloc(size_t size,size_t& nobjs//20)
{
size_t totalBytes = nobjs*size;
size_t leftBytes = finish-start;
void * res =null;
if(leftBytes > = totalBytes) //內(nèi)存池空間足以提供20
{res = start;start+=totalBytes;return res;}
else if(leftBytes>=size)//不夠20至少提供1塊
{nobjs =leftBytes/size;res=start;start+=nobjssize;return res;}//實際能提供多少塊
else //內(nèi)存池的空間也不足,連一塊也提供不了
{
//1.將內(nèi)存池中剩余的內(nèi)存—>掛接到相應桶中
//total_get = 2total_bytes+RoundUP(heap_size>>4);
}
}
二級空間配置器中沒有釋放空間
空間配置器的默認選擇
SGI-STL默認使用一級還是二級空間配置器,通過USE_MALLOC宏進行控制:
#ifdef __USE_MALLOCtypedef malloc_alloc alloc;typedef malloc_alloc single_client_alloc; #else// 二級空間配置器定義 #endif在SGI_STL中該宏沒有定義,因此:默認情況下SGI_STL使用二級空間配置器
空間配置器的再次封裝
在C++中,用戶所需空間可能是任意類型的,有單個對象空間,有連續(xù)空間,每次讓用戶自己計算所需空間總大小不是很友好,因此SGI-STL將空間配置器重新再封裝了一層:
template<class T, class Alloc> class simple_alloc { public:// 申請n個T類型對象大小的空間static T *allocate(size_t n){return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof (T));}// 申請一個T類型對象大小的空間static T *allocate(void){return (T*)Alloc::allocate(sizeof (T));}// 釋放n個T類型對象大小的空間static void deallocate(T *p, size_t n){if (0 != n)Alloc::deallocate(p, n * sizeof (T));}// 釋放一個T類型對象大小的空間static void deallocate(T *p){Alloc::deallocate(p, sizeof (T));} };對象構造與釋放
一切為了效率考慮,SGI-STL決定將空間申請釋放和對象的構造析構兩個過程分離開,因為有些對象的構造不需要調用析構函數(shù),銷毀時不需要調用析構函數(shù),將該過程分離開可以提高程序的性能:
// 歸還空間時,先先調用該函數(shù)將對象中資源清理掉 template <class T> inline void destroy(T* pointer) {pointer->~T(); } // 空間申請好后調用該函數(shù):利用placement-new完成對象的構造 template <class T1, class T2> inline void construct(T1* p, const T2& value) {new (p)T1(value); }總結
以上是生活随笔為你收集整理的详解STL中的空间配置器(SGI版本)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都大熊猫繁育基地要坐观光车吗
- 下一篇: 黎明觉醒激活信号塔有什么用