以STL運用的角度而言,空間配置器是最不須要介紹的,它老是藏在一切組件的背後,默默工做。整個STL的操做對象都存放在容器之中(vertor、list),而容器必定須要配置空間以放置資料,這就是空間配置器的做用。c++
雖然STL提供了讓咱們自定義空間配置器的接口,可是不建議本身定義,由於標準提供的空間配置器是安全的,且效率也不錯的。因此咱們使用時,通常都會使用默認的配置器。以下:數組
template <class T, class Alloc = allocator<T> > class vector {}; vect<int> vec; //這裏只傳入int類型,使用默認的空間配置器
下面的空間配置器是按照SGI 版本的STL進行講解的,可是STL的原理是通的。安全
經過前面整理C++ new和delete的詳解,咱們知道C++內存配置操做和釋放操做是這樣的:函數
class Foo {...}; Foo* pf = new Foo; //配置內存,而後構造對象 delete pf; //將對象析構,而後釋放內存
這其中的 new 內含兩個階段操做:一、調用operator new 配置內存。二、調用構造函數,構造對象內容
delete也內含兩個階段操做:一、調用析構函數。二、調用operator delete 釋放內存。性能
爲了精密分工,STL 將這兩個階段操做區分開來。內存配置操做由 成員函數 alloccate() 負責,內存釋放由 deallcate() 負責;對象構造由 construct() 負責,對象析構則由 destroy() 負責。spa
在內存分配的過程當中,會有幾個問題須要考慮。
一、小塊內存帶來的內存碎片問題。
二、小塊內存頻繁申請釋放帶來的性能問題。.net
爲了解決這些問題,SGI STL設計了 雙層級配置器,也就是第一級配置器和第二級配置器。第一級配置器直接使用 malloc() 和 free() ,第二級配置器則視狀況採用不一樣的策略:當配置區塊超過128 bytes 時,視之爲 「足夠大」,便調用第一級配置器;當配置區塊小於 128 bytes 時,視之爲 「太小」 ,爲了下降額外負擔,便採用複雜的 內存池 管理方式。設計
第一級配置器的流程以下:指針
SGI的第一級配置器以 malloc(), free(), realloc() 等C函數執行實際的內存配置、釋放、重配置操做。當 malloc 或者 realloc 調用不成功後,改調用 oom_malloc() 和 oom_realloc() 。後二者都有內循環,不斷調用「內存不足處理例程」,指望在某次調用以後,得到足夠的內存。但若是「內存不足處理例程」未被客戶端設定,則直接拋出 bad_alloc 異常,或者終止程序。code
注意:設計內存不足處理例程是客戶端的責任,設定內存不足處理例程也是客戶端的責任。
二級配置器使用內存池+自由鏈表的形式避免了小塊內存帶來的碎片化,提升了分配的效率,提升了利用率。它是用一個16個元素的自由鏈表(free_list)來管理的,每一個位置的內存大小都是8的倍數,分別爲:八、1六、2四、3二、40、4八、5六、6四、7二、80、8八、9六、10四、1十二、120、128。
free_list的節點結構以下:
union obj { union obj* free_list_link; char client_data[1]; };
使用union是爲了節省內存,這樣每一個節點就不須要額外的指針。
內存池與自由數組 free_list 之間的關係以下圖所示:其中free_list的第一個元素指向 8個字節的空間,8個字節的空間我給分配了10個。free_list的最後一個元素指向128個字節的空間,此空間我給分配了4個。
free_list管理的是內存池中已經分配給 free_list 且還沒有使用的內存,若是系統想從free_list中拿一8字節內存,則直接從free_list[0]中彈出頂部第一個元素,而後頂部後移。
主要分爲四種狀況:
一、free_list列表中有空餘內存。若是申請3個字節的內存,則所需空間大小提高爲8的倍數,而後去 free_list 中查找相應的鏈表,若是 free_list[i] 不爲空,則返回第一個元素,而後把頭指針日後移。
二、free_list 列表中沒有空餘,但內存池不爲空。首先檢驗內存池中的大小是否是比申請的內存大,好比申請20*8的內存,若是足夠,則分配相應內存,將其中一個分配給用戶使用,其它的掛在相應的 free_list 中。若是內存池不夠大,只夠幾個內存分配,則就分配這幾個,把相應的數據返回。若是連一個都不夠則執行第三中狀況。
三、free_list列表中沒有空餘,內存池也不夠。調用malloc從新分配內存,分配時會多分配一倍的內存,把相應的內存掛到free_list下,剩餘的放到內存池中。
四、free_list列表中沒有空餘,內存池也不夠,malloc也失敗。則調用一級空間配置器,裏面會有循環處理,或者拋出異常。
當用戶從二級空間配置器中申請的內存被釋放時,二級空間配置器將回收的內存插入到對應的 free_list 中。其流程以下:
咱們知道,引入相對複雜的空間配置器,主要源自兩點:
一、頻繁使用malloc、free開闢釋放小塊內存帶來的性能效率的低下
二、內存碎片問題,致使不連續內存不可用的浪費
引入兩層配置器幫咱們解決了以上的問題,可是也帶來一些問題:
一、內存碎片的問題,自由鏈表所掛區塊都是8的整數倍,所以當咱們須要非8倍數的區塊,每每會致使浪費。
二、咱們並無釋放內存池中的區塊。釋放須要到程序結束以後。這樣子會致使自由鏈表一直佔用內存,其它進程使用不了。
STL下的空間配置器分位兩級,他們沒有高低之分,只有一個條件,當用戶所須要的的內存大小: