sgi stl 的配置器與眾不同,也與標準規範不同,其名稱是 alloc 而非 allocator ,而且不接受任何引數。
我們所習慣的c++記憶體配置操作和釋放操作如下:
class foo ;
foo* pf = new foo; // 配置記憶體,然後構造物件
delete pf; // 將物件析構,然後釋放記憶體
這其中的new 操作符(new operator)包含兩階段操作:
(1)呼叫operator
new配置記憶體
(2)呼叫foo::foo( )建構函式構造物件內容。
delete操作符包含兩階段操作:
(1)呼叫foo::~foo( )析構函式將物件析構。
(2)呼叫operator
delete釋放記憶體
注:如果只是進行空間分配操作,那麼使用operator new就可以了,就好比c的malloc函式;如果已經分配好了空間,想在上面構造乙個物件,那麼可以使用placement new。
stl allocator 將這兩階段操作區分開來。記憶體配置操作由 alloc::allocate() 負責,記憶體釋放操作由 alloc::deallocate() 負責;物件構造操作由 ::construct() 負責,物件析構操作由 ::destroy() 負責。
記憶體空間的配置/釋放與物件內容的構造/析構,分別著落在和
這兩個檔案身上。此外還有乙個檔案
,這裡定義了一些全域性函式,用來填充(fill)或複製(copy)大塊記憶體資料,它們也都隸屬於stl標準規範。
總結:
stl空間配置器主要分三個檔案實現:
(1):這裡定義了全域性函式construct()和destroy(),負責物件的構造和析構。
(2):檔案中定義了
一、二兩級配置器,彼此合作,配置器名為alloc。
(3):這裡定義了一些全域性函式,用來填充(fill)或複製(copy)大塊記憶體資料,他們也都隸屬於stl標準規範。
stl提供了五個全域性函式用於處理空間,分別為:
1. construct 用於構造;
2. destroy 用於析構;
3. uninitialized_copy(first, last, result) 將[first,last)
範圍內的物件複製到result處;
4. uninitiated_fill(first, last, x) 將[first,last)範圍內的內
存用物件x的副本填充;
5. uninitiated_fill_n(first, n, x) 將first開始的n個連續的記憶體
空間用x的副本填充;
簡單來說,alloc主要在如下方面超越了allocator
1.通過記憶體池技術提公升了分配效率:
2.對小記憶體頻繁分配所可能造成的記憶體碎片問題的處理
3.對記憶體不足時的處理
sgi stl在
中定義了兩級配置器。第一級空間配置器使用malloc/free函式,當分配的空間大小超過128 bytes的時候使用第一級空間配置器;第二級空間配置器使用了記憶體池技術,當分配的空間大小小於128 bytes的時候,將使用第二級空間配置器。
當使用者申請大區塊時,它將其交予第一級配置器。當使用者申請小區塊時,將與記憶體池打交道,記憶體池通過自由鍊錶(free-list)來管理小區塊,當記憶體池不足時,會一次性向堆中申請足夠大的空間。使用者可以通過巨集來控制使用哪一級配置器(預設為二級配置器)。
static void
* allocate(size_t __n)
else
}return __ret;
};
大量分配小塊的記憶體空間會帶來問題:一是從執行庫的堆管理器中取得的記憶體(如通過malloc獲得),會有一部分空間用於儲存管理資訊,用於管理各個記憶體塊,導致記憶體的使用率降低;二是過多的小塊記憶體會帶來記憶體碎片問題;採用合適的記憶體池技術可以避免這些問題。
配置器除了負責配置,也負責**。為了管理方便,sgi第二級配置器會主動將任何小額區塊的記憶體需求量上調至8的倍數。並維護16個free-lists,各自管理大小分別為8,16,24,32,40,48,56,64,72,80,88,96,104, 112,120,128 位元組的小額區塊。當申請小於等於128位元組時就會檢查對應的free list,如果free-list中有可用的區塊,就直接拿來,如果沒有,就準備通過refill為對應的free-list 重新填充空間。free-list的節點結構如下:
union obj
;
這裡使用union結構,是為了節省空間。
空間的**,則是把記憶體重新加入到free-list對應的節點鍊錶上去。
前面提到,如果free-list中沒有可用的區塊,就通過refill為對應的free-list 重新填充空間。此時refill進行了怎樣的操作呢?
預設操作是通過_s_chunk_alloc
從記憶體池中取得20個新的節點新增到free-list鍊錶中,而如果記憶體池中的記憶體不夠用,這時候能分多少就分多少節點,返回的相應的節點數。再萬一記憶體池乙個節點都提供不了,就給記憶體池新增空間,如果失敗,再丟擲bad_alloc異常。
template
void*
__default_alloc_template<__threads, __inst>::_s_refill(size_t __n)
else
}return(__result);
}
通過_s_chunk_alloc
,從記憶體池中分配空間給free-list。_s_chunk_alloc
的流程總結如下:
1、 記憶體池有足夠大小的空間,則分配申請的空間;
2、 記憶體池沒有足夠大小的空間,但是至少還能分配乙個節點的空間,則能分多少分多少;
3、 記憶體池乙個節點都騰不出來,向系統的heap申請2倍於要求大小的空間,在
此之間,如果記憶體池剩餘有空間,則放到free-list中去;
4、 如果向heap申請空間失敗,那麼只能看free-list中更大的節點是否有可
用空間了,有則用之,同時遞迴呼叫自身修正__nobjs;
5、 如果free-list也沒有可用節點了,那麼轉向第一級空間配置器申請空間;
6、 再不行,第一級空間配置器就丟擲bad_alloc異常。
如果有需求的話,記憶體池中會不斷的通過malloc申請新的記憶體,最後記憶體池所擁有的記憶體也會越來越大,當然最後程序結束的時候,這些記憶體都會由作業系統收回。
部分參考自
《STL原始碼剖析學習筆記》
1 演算法泛化過程 首先是模板 適應不同型別 其次是迭代器 具有原生指標行為的類 最後是仿函式 行為類似函式指標,但更適合泛化 泛化的優點 個人總結 1 簡化程式,提高 的可重用性 2 提高了程式的通用性 3 提高了程式的可讀性 4 提高了安全性 函式指標及仿函式 首先說說函式指標,乙個程式可能需要...
《STL原始碼剖析》學習筆記
第二章 空間配置器 allocator 考慮到小型區塊可能造成的記憶體破碎問題,sgi設計了雙層級配置器。當配置區塊超過128bytes時,便呼叫第一級配置器,即直接使用malloc free sgi的第一級配置器的allocate 和realloc 都是在呼叫malloc 和realloc 不成功...
STL原始碼剖析筆記
在stl中,迭代器使得容器與演算法分離開來,迭代器會定義五種型別。hash table是一種資料結構,和rb tree類似。其中set map multi set multi map都是基於rb tree。在stl中,patial sort 和nth element 不一樣。patial sort是...