第二級配置器用於分配小於128 bytes的內存申請,經過本身管理一塊內存區域,來減小申請大量小塊內存的系統調用和內存COOKIE消耗。c++
整個第二級配置器的核心就是free_list和內存池。
其中,free_list爲一個個8n bytes大小的區塊,每當申請內存時,首先查找free_list是否存在相應的區塊可供分配。
內存池是一大塊連續的內存,咱們經過首地址和尾地址進行管理。
咱們能夠簡單地理解爲這樣一個三級結構:函數
free_list
內存池
操做系統源碼分析
free_list不夠用時,從內存池裏打水。
內存池不夠用時嗎,向操做系統申請開閘放水。
第二級配置器基本都是在管理這個三級結構。操作系統
if(n>(size_t) __MAX_BYTES) { return (malloc_alloc::allocate(n)); }
C++提供static這個關鍵詞對靜態成員進行聲明,靜態成員函數和類的實例化無關,對於同一類來講,靜態成員函數是共享的。而普通成員函數須要實例化才能調用,對於每個實例來講,普通成員函數是本身獨有的。指針
return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
bytes是傳入參數,__ALIGN==8。
原式即 (bytes+0111) & 1000
會使返回值在bytes的基礎上向上以8爲單位取整。code
return (((bytes) + __ALIGN-1) / __ALIGN - 1);
這個式子比上式少了一個取反並且變成了除法。
同理,原式即 (bytes+7)/8 - 1
能夠得知返回值是一個大於bytes的最小的8的倍數。
用於獲取不一樣大小的空閒區塊。遞歸
這兩個函數是STL要求的接口。
這裏的實現是根據申請的內存空間的大小,分別調用不一樣的方法來申請內存。若是大於128bytes就使用第一級配置器,小於就使用內存池方法。
使用內存池方法在這裏就是直接使用上述的兩個函數來獲取相應大小的鏈表首地址(咱們管理了一個free_list)。若是該大小的鏈表爲空,則調用refill來填充鏈表。接口
template <bool threads, int inst> void *__mempool_alloc_template<threads,inst>::refill(size_t n) { //todo }
這個函數及以後的部分就是對free_list的管理了。
經過調用chunk_alloc來獲取區塊,這個過程就是在維護並更新一個free_list。內存
template <bool threads, int inst> char *__mempool_alloc_template<threads,inst>::chunk_alloc(size_t size, int & nobjs) { //todo }
我以爲這個函數應該放在refill前看,由於這個函數是refill的基礎之一。這個函數的功能就是從咱們已經掌握的內存池中劃出一塊,去維護一個可用的free_list。
首先該函數判斷內存池剩餘空間,若是有剩餘,會分配給free_list。這時有三種狀況,先說前兩種,一種是徹底夠用,一種是隻夠一個區塊使用。對於前者分配直接返回,對於後者,咱們先分配一塊再說。
第三種狀況,就是最糟糕的,內存池連一個區塊都劃不出了。那咱們就須要往內存池中分配內存,這個分配內存,是指經過系統調用來分配內存,而不是咱們本身管理本身已經向操做系統申請的大塊內存。
這時要分兩步走。第一步是將內存池中剩餘的空間給利用的明明白白,由於咱們管理內存池時是靠首地址和尾地址來管理的,即咱們的內存池是一大塊連續的內存,咱們要先把這塊內存用完才能向操做系統申請並管理另一大塊連續內存。
第二步是向操做系統申請內存了。最完美的狀況就是一步到位。可是若是申請不到內存呢?
咱們特別注意一個狀況,咱們已經把內存池清空了,這時咱們內存池是什麼都沒的,也就是首地址==尾地址。咱們要尋找更多的內存,只有向free_list裏尋找了,咱們能夠釋放free_list裏大區塊的內存來知足咱們較小區塊的內存要求,好比我須要申請2個8b的區塊,這時咱們能夠釋放一個16b的區塊,而後再把這個16b的區塊切成兩個來知足需求。
這裏利用了一個遞歸調用本身的機制。
可是若是仍然找不到內存呢?那咱們只能求助於第一級分配器的out-of-memory機制了。宣告我已經無能爲力了!源碼