tl中各種容器都有乙個可選的模板引數:allocator,也就是乙個負責記憶體分配的元件。stl標準規定的allcator
被定義在memory檔案中。stl標準規定的allocator只是單純地封裝operator new,效率上有點過意不去。
sgi實現的stl裡,所有的容器都使用sgi自己定義的allocator。這個allocator實現了乙個small object的記憶體池。
loki裡為了處理小物件的記憶體分配,也實現了類似的記憶體管理機制。
該記憶體池大致上,就是一大塊一大塊地從系統獲取記憶體,然後將其分成很多小塊以鍊錶的形式鏈結起來。其內部
有很多不同型別的鍊錶,不同的鍊錶維護不同大小的記憶體塊。每一次客戶端要求分配記憶體時,allcator就根據請求
的大小找到相應的鍊錶(最接近的尺寸),然後從煉表裡取出記憶體。當客戶端歸還記憶體時,allocator就將這塊記憶體
放回到對應的煉表裡。
我簡單地畫了幅圖表示整個結構:
allocator內部維護乙個鍊錶陣列,陣列元素全部是煉表頭指標。鍊錶a每乙個節點維護乙個8bytes的記憶體塊,鍊錶
b每乙個節點維護乙個16bytes的記憶體塊。
當客戶端請求分配10bytes的記憶體時,allocator將10調整為最接近的16bytes(只能大於10bytes),然後發現16bytes
這個鍊錶(鍊錶b)裡有可用記憶體塊,於是從b裡取出一塊記憶體返回。當客戶端歸還時,allocator找到對應的鍊錶,將
記憶體重新放回鍊錶b即可。
大致過程就這麼簡單,也許有人要說用鍊錶維護一塊記憶體,鍊錶本身就會浪費一些記憶體(在我很早前接觸記憶體池時,
總會看到類似的論點= =|),其實通過一些簡單的技巧是完全可以避免的。例如,這裡allocator維護了很多記憶體塊,
反正這些記憶體本身就是閒置的,因此我們就可以直接在這些記憶體裡記錄鍊錶的資訊(下乙個元素)。
還是寫點**詳細說下這個小技巧:
obj;
void
*mem
=malloc(
100);
obj
*header
=(obj
*) mem;
obj
*cur_obj
=header;
obj
*next_obj
=cur_obj;
for(
inti =0
; ;
++i )
else
free( mem );
這樣,通過header指標和next域,就可以逐塊(這裡是10byts)地訪問mem所指向的記憶體,而這些鍊錶的節點,都
是直接儲存在這塊記憶體裡的,所以完全沒有額外消耗。
我用c模仿著sgi的這個allocator寫了個可配置的記憶體池,在其上按照stl的標準包裝了乙個allocator,可以直接
用於vc自帶的stl裡。
測試**
稍微測試了下,發現在不同的機器上有明顯的差距。
SGI STL空間配置器和記憶體池
最近在看侯捷老師的 stl原始碼剖析 非常感嘆其中空間配置器實現的巧妙和細緻,對效率真正是錙銖必較。一般我們所習慣的記憶體配置和釋放是通過new和delete來完成的,而new運算包含了兩個階段 1.呼叫 operator new配置記憶體 2.呼叫建構函式 foo 構造物件。delete運算也包含...
SGI STL記憶體分配管理
大多數時候,分配記憶體一般是採用malloc或new,stl記憶體分配是怎麼樣的呢?stl的記憶體採用的是 兩級記憶體配置方案。流程如下 第一級記憶體方案,就是malloc 為什麼不用new?歷史原因 c 不提供realloc方法,詳見這裡就不在贅述了,下面重點講下第二級記憶體池方案。實際上,第二級...
記憶體池 C 記憶體池
c c 下記憶體管理是讓幾乎每乙個程式設計師頭疼的問題,分配足夠的記憶體 追蹤記憶體的分配 在不需要的時候釋放記憶體 這個任務相當複雜。1.呼叫malloc new,系統需要根據 最先匹配 最優匹配 或其他演算法在記憶體空閒塊表中查詢一塊空閒記憶體,呼叫free delete,系統可能需要合併空閒記...