SGI STL空間配置器和記憶體池

2021-06-07 21:26:35 字數 2680 閱讀 6827

最近在看侯捷老師的《stl原始碼剖析》,非常感嘆其中空間配置器實現的巧妙和細緻,對效率真正是錙銖必較。

一般我們所習慣的記憶體配置和釋放是通過new和delete來完成的,而new運算包含了兩個階段:1.呼叫::operator new配置記憶體 2.呼叫建構函式 foo() 構造物件。delete運算也包含兩個階段:1.呼叫析構函式 ~foo() 將物件析構 2.呼叫::operator delete釋放記憶體。

1

class

foo ;

2 foo *pf = new

foo;

3 delete pf;

而sgi stl為了提高效率則是把二者分開的,物件的構造的析構由 construct() 和 destroy() 完成,記憶體的配置則是由std::alloc完成,alloc是sgi stl中的預設空間配置器,它是具有次層配置能力的,分為一級配置器和二級配置器,一級配置器直接由 malloc() 和 free() 這兩個 c 函式完成,二級配置器就複雜了,它根據不同的情況而採取不同的策略,當申請的記憶體區塊大於128bytes時,就移交給一級配置器處理,小於128bytes則由二級配置器採用複雜的記憶體池( memery pool )完成,這主要是為了減少記憶體碎片的產生,而且還能降低額外負擔,什麼負擔呢?我們每申請一塊記憶體,都要向系統上繳一部分「稅」,當然這些稅取之於民也用之於民,主要是用來記錄記憶體的相關資訊,如大小之類的,所以當記憶體越小時這種額外的負擔就越大。

一級配置器會在記憶體不足 ( out-of-memory) 時,呼叫oom_malloc(),執行記憶體不足處理例程,__malloc_alloc_oom_handler() = 0; 不斷嘗試釋放其它記憶體,請注意該處

理例程是要使用者自己設計

的並且要自己設定的!否則系統會毫不客氣的呼叫 _throw_bad_alloc,丟擲 bad_alloc 異常資訊,或者直接 exit(1) 來終止程式!其中__throw_bad_alloc是乙個巨集:

1

#if 02

# include

>

3 # define __throw_bad_alloc throw

bad_alloc

4 #elif

!define( __throw_bad_alloc)

5 # include

6 #define

__throw_bad_alloc cerr<

#endif

然後我們看二級配置器,它是以記憶體池 ( memory pool ) 來管理的:它由16個自由鍊錶構成,每次配置一大塊記憶體,並維護對應之自由鍊錶 ( free-lists ),下次若有相同大小的記憶體請求,就直接從free-lists 中撥出這塊記憶體,同時,如果客端釋放小額區塊時,剛把它**到對應大小的自由鍊錶 ( free-lists )中。

這16個肩負重任的 free-lists 分別管理大小為 8,16,24,32,40,56,64,72,80,88,96,104,112,120,128 bytes 大小的小額區塊,所以當申請小額記憶體時,記憶體的需求量會被上調至 8 的倍數,以便於管理。接下來記憶體池就要登場了……

首先由空間配置函式 allocate() 進行空間的配置,如果能夠在 free-lists 中找到對應大小的區塊,就把對應的區塊呼出,返回。否則就要呼叫 refill() 對 free-lists 進行填充,新的空間來自記憶體池,由 chunk_alloc() 完成。預設是取得20個新區塊,但如果記憶體池空間不足數量會小於20。取得的新區塊,把第一塊 return ,剩餘的則維護到free-list中。

我們看下 chunk_alloc() 是怎麼工作的:

從記憶體池是取出新空間可能發生三種情況

1. 記憶體池剩餘量完全滿足需求量

2. 記憶體池剩餘量不能完全滿足需求量,但足夠**  >= 1 個 區塊

3. 記憶體池剩餘量乙個區塊都不能**

1

chunk_alloc(size, nobjs)2

7 else

if(bytes_left >= size) //

2.能**至少乙個區塊

8

9 else

12 }

首先我們看第一種情況:記憶體池剩餘量完全滿足需求量

這種情況是最好的情況,直接返回所需的區塊就可以了

第二種情況:記憶體池剩餘量不能完全滿足需求量,但足夠**  >= 1 個 區塊

這種情況也好辦,我們只要計算出實際能夠**的區塊的個數,然後返回這些區塊就可以了

剩下最後一種情況:記憶體池剩餘量乙個區塊都不能**

這種情況比較麻煩了,我們首先要進行 「善後」 , 把剩餘的這些殘餘的空間**到 free-lists 中,然後才申請分配新的空間。申請新空間的大小為所需空間的2倍,再加上乙個隨著配置次數增加而增加的附加量。記憶體池向堆空間索要記憶體,用malloc(), 如果成功,則調整記憶體池的起點和終點,遞迴呼叫 chunk_alloc() 就行了,否則如果分配記憶體失敗,就要想法從其它地方找點記憶體來用了,沒錯!就是 free-lists,向那些較大,且有剩餘區塊沒用的free-list索要記憶體,如果找到了,就挖一塊返回,呼叫chunk_alloc() 返回區塊,否則就沒辦法了,只能呼叫一級配置器了,因為那裡有out-of-memory處理例程,興許還能找出點記憶體,如果能找到,那最好了,呼叫chunk_alloc()把區塊返回給refill(),返回結果並調整free-lists,否則,真是天要亡我啊!只能丟擲異常了……

SGI STL的記憶體池

tl中各種容器都有乙個可選的模板引數 allocator,也就是乙個負責記憶體分配的元件。stl標準規定的allcator 被定義在memory檔案中。stl標準規定的allocator只是單純地封裝operator new,效率上有點過意不去。sgi實現的stl裡,所有的容器都使用sgi自己定義的...

stl記憶體池剖析空間配置器

原 參考stl庫 具體只有記憶體分配 類似於c 的newhander 部分 沒有列出 stl空間配置器的記憶體池模型 pragma once include include include using namespace std class mallocalloctemplate 定義一級空間配置器...

SGI STL空間配置器(STL原始碼剖析)

空間配置器的標準介面 根據 stl規範 allocator value type allocator pointer allocator const pointer allocator reference allocator const reference allocator size type a...