《STL 源碼剖析》之空間配置器

首先,這個爲什麼叫“空間配置器”而不叫“內存配置器”呢?因爲空間不一定是內存,也可以是磁盤或者其它存儲介質。

下面我們來看一段代碼:

	class Foo{ ... };
	Foo *p = new Foo;
	delete p;

這在C++中是很普遍的內存申請與釋放操作。其中new算式內含兩個階段的操作:
(1)調用::operator new配置內存,即申請內存
(2)調用Foo::Foo()構造函數,構造對象
同樣的delete算式也包含兩個階段的操作:
(1)調用Foo::~Foo()將對象析構
(2)調用::operator delete釋放內存
爲了精密分工,STL allocator將這兩階段操作區分開來。內存配置操作由alloc::allocate()負責,內存釋放操作由alloc::deallocate()負責;對象構造由::construct()負責,對象析構操作由::destroy()負責。下面分別進行介紹。
::constuct()沒有什麼好說的,對於::destroy(),有兩個版本,第一個版本傳入指針參數,調用類本身的析構函數即可;第二個版本,接受兩個迭代器,first和last,準備將該區間內所以對象都析構掉。我們並不知道這個範圍有多大,如果很大,而每個對象的析構函數都無關痛癢(比如析構函數爲空),那麼一次次調用這些無關痛癢的析構函數,對效率是一種傷害。爲了解決這個問題,底層提供了一種方法,用來判斷析構函數是否爲trivial,這個後面會介紹(traits)。
空間的配置與釋放,哲學思想有如下四個方面:
(1)向system heap要求空間
(2)考慮多線程狀態
(3)考慮內存不足時的應變措施
(4)考慮過多“小型區塊”可能造成的內存碎片問題
C++的內存配置基本操作是::operator new(),內存釋放基本操作是::operator delete()。其實這兩個全局函數相當於C的malloc() 和 free() 函數。SGI正是以這兩個函數完成內存的配置與釋放。
對於上面所述第四點,即小型區塊所可能造成的內存破碎問題,SGI設計了雙層級配置器。第一級直接使用malloc()與free()函數。第二層級視情況不同採用不同的策略:
當配置區塊超過128bytes時,視之爲“足夠大”,便調用第一級配置器;當配置區塊小於128bytes時,視之爲“過小”,爲了降低額外負擔,便採用memory pool方式處理。具體如下:
每次配置一大塊內存,並維護對應的自由鏈表。下次若再有相同大小的內存需求,就直接從free-lists中拔出。如果客端釋放小額區塊,就加入到free-lists中。同時爲了方便管理,會將任何小額區塊的內存需求量上調至8的倍數。
有人可能會提到,爲了維護鏈表,每個節點需要額外的指針(即指向下一個節點),這又會造成額外負擔。這裏提供瞭解決方法:

	union obj{
    		union obj * free_list_link;
    		char client_data[1];
	}
上述obj所用的是union,由於union的特性,從第一個字段觀之,obj可被視爲一個指針,指向相同形式的另一個obj。從其第二字段觀之,obj可被視爲一個指針,指向實際區塊。
其實這個自由鏈表和操作系統中內存碎片管理頗爲相似,操作系統中也是有一個鏈表,把內存碎片排序連起來。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章