1.allocator是空間配置器而不是記憶體配置器,空間不一定是記憶體,也可以是磁碟或其他輔助儲存介質。但sgi stl提供的配置器配置的物件是記憶體。
2.sgi標準的空間配置器,std::alloctor
sgi定義了乙個符合部分標準,名為alloctor的配置器,效率不高,只把c++的::operator new 和 ::operator delete做了一層薄薄的封裝。
3.c++用new構造乙個物件時,包含兩階段操作:(1)呼叫::operator new 配置記憶體;(2)呼叫該物件的建構函式構造物件內容。 delete銷毀乙個物件時,也包含兩階段操作:(1)呼叫該物件的析構函式將物件析構;(2)呼叫::operator delete釋放記憶體。
stl allocator將上述兩階段操作區分開來。記憶體配置由alloc::allocator()負責,記憶體釋放操作由alloc::deallocator()負責;物件構造由::constructor()負責,物件析構由::destroy負責。
4.構造和析構基本工具
物件的構造:
template
inline
void construct(t1* p, const t2& value)
上述函式接受乙個指標p和乙個初值value,用定位new運運算元將初值設定到指標所指的空間上。
物件的析構:
template
//第一版本
inline
void destroy(t* pointer)
template
//第二版本
inline
void destroy(forwarditerator first, forwarditerator last)
template
inline
void
__destroy_aux(forwarditerator first, forwarditerator last, __false_type)
template
inline
void __destroy_aux(forwarditerator, forwarditerator, __true_type) {}
上述destroy()的第一版本接受乙個指標,將指標所指的物件析構掉。第二版本接受first和last兩個迭代器,將兩個迭代器範圍內的物件析構掉。在第二版本中運用了traits程式設計技法,traits會得到當前物件的一些特性,再根據特性的不同分別對不同特性的物件呼叫相應的方法。在第二版本中,stl會分析迭代器所指物件的has_trivial_destructor特性的型別(只有兩種:__true_type和__false_type),如果是__true_type,stl就什麼第一不做;如果是false_type,就會呼叫每個物件的析構函式來銷毀這組物件。
5.記憶體的配置
sgi設計了雙層配置器,第一級配置器直接使用malloc()和free(),第二級配置器採取以下策略:當配置區塊超過128bytes時,呼叫第一級配置器;當配置區塊小於128bytes時,採用記憶體池方式。
6.第一級配置器
static
void * allocate(size_t n)
static
void deallocate(void *p, size_t /* n */)
static
void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
當alloc()和realloc()申請不到記憶體時,會呼叫oom_malloc()和oom_reaalloc(),這兩個函式不斷呼叫「記憶體不足處理函式」,直到獲得足夠的記憶體為止。如果使用者沒有傳遞「記憶體不足處理函式」,會丟擲__throw_bad_alloc異常。
7.第二級配置器
第二級配置器的做法:如果申請記憶體大於128bytes時,就用第一級配置器。小於128bytes時,以記憶體池管理:每次配置一大塊記憶體,並維護相應的自由鍊錶。
具體分配過程:
allocate()函式處理過程:
1)如果申請記憶體大於128bytes,就呼叫第一級配置器,否則說明申請記憶體小於128bytes,轉到2)
2)根據申請記憶體的大小n在16個free lists中找到其對應的my_free_list
3)如果對應的my_free_list中沒有空閒區塊,分配器首先將申請的記憶體大小上調至8的倍數n,呼叫refill(),準備重新填充my_free_list
4)否則說明有可用的空閒區塊,更新my_free_list並將第一塊空閒區塊返回
refill()函式的處理過程:
1)呼叫chunk_alloc()函式申請20*n的記憶體空間(不一定取得到)
2)如果只獲得大小為n的區塊,這個區塊就分配給呼叫者,否則從獲得的區塊中取出一塊分配給呼叫者,其餘的用my_free_list串接起來
chunk_alloc()函式處理過程:
1)如果記憶體池剩餘空間大於或等於20*n的記憶體空間,則從這個空間中取出n*20大小的記憶體空間,更新start_free並返回申請到的記憶體空間的其實位址,否則轉到2)
2)如果記憶體池剩餘空間足夠分配乙個及以上的區塊,則分配整數倍於n的記憶體空間,更新start_free,由nobjs返回實際分配到的區塊個數,並返回申請到的記憶體空間的其實位址,否則轉到3)
3)記憶體池中無法提供乙個大小為n的區塊,此時如果記憶體池中還有一些殘餘記憶體(這些記憶體大小小於n),則將這些記憶體插入到其對應大小的空閒分割槽鏈中
4)呼叫malloc向執行時庫申請大小為(2*20*n+附加量)的記憶體空間,如果申請成功,更新start_free,end_free 和 heap_size,並遞迴呼叫chunk_alloc(),修正bobjs,否則轉到5)
5)4)中呼叫malloc失敗,此時分配器依次遍歷區塊足夠大的free_lists,只要有乙個未用區塊,就釋放該區塊,遞迴呼叫chunk_alloc(),修正nobjs
6)如果出現意外,到處都沒有記憶體可用了,則呼叫第一級配置器,看out-of-memory機制能否盡點力
對應第二級配置器的allocate()函式處理過程的第4步:
對應第二級配置器的記憶體池操作:
8.記憶體的釋放
具體記憶體釋放過程:
先判斷要釋放的背刺區塊的大小,大於128bytes就呼叫第一級配置器釋放記憶體,否則要釋放的記憶體區塊小於128bytes,就找出相應的free_list,將區塊**。
STL原始碼剖析 空間配置器
看過stl空間配置器的原始碼,總結一下 1 stl空間配置器 主要分三個檔案實現,stl construct.h 這裡定義了全域性函式construct 和destroy 負責物件的構造和析構。stl alloc.h檔案中定義了 一 二兩級配置器,彼此合作,配置器名為alloc.stl uninit...
STL原始碼剖析 空間配置器
由於物件的建立分為分配記憶體和呼叫建構函式兩部分,stl allocator使用alloc allocate 來分配記憶體,construct 構造物件。construct 函式只有乙個泛化的版本,destroy 函式有乙個泛化的針對迭代器的版本,destroy aux 根據是否需要呼叫析構函式進行...
STL原始碼剖析 空間配置器
我 然後開始聊vector動態擴容,o sgi stl的配置器與眾不同,名稱是alloc而非allocator。標準的allocator只是對操作符new和delete的一層薄薄的封裝,並沒有考慮到任何效率上的優化。一般而言,我們習慣的c 記憶體配置操作和釋放操作是這樣的 class foo foo...