STL空間配置器

2021-08-20 07:15:56 字數 2845 閱讀 9508

為什麼要有空間配置器呢?這主要是從兩個方面來考慮的。

1、小塊記憶體帶來的記憶體碎片問題

單從分配的角度來看。由於頻繁分配、釋放小塊記憶體容易在堆中造成外碎片(極端情況下就是堆中空閒的記憶體總量滿足乙個請求,但是這些空閒的塊都不連續,導致任何乙個單獨的空閒的塊都無法滿足這個請求)。

2、小塊記憶體頻繁申請釋放帶來的效能問題。

關於效能這個問題要是再深究起來還是比較複雜的,下面我來簡單的說明一下。

開闢空間的時候,分配器會去找一塊空閒塊給使用者,找空閒塊也是需要時間的,尤其是在外碎片比較多的情況下。如果分配器其找不到,就要考慮處理假碎片現象(釋放的小塊空間沒有合併),這時候就要將這些已經釋放的的空閒塊進行合併,這也是需要時間的。

malloc在開闢空間的時候,這些空間會帶有一些附加的資訊,這樣的話也就造成了空間的利用率有所降低,尤其是在頻繁申請小塊記憶體的時候

stl裡面的空間配置主要分為兩級,一級空間配置器(__malloc_alloc_template)和二級空間配置器(__default_alloc_template)。在stl中預設如果要分配的記憶體大於128個位元組的話就是大塊記憶體,呼叫一級空間配置器直接向系統申請,如果小於等於128個位元組的話則認為是小記憶體,則就去記憶體池中申請。一級空間配置器很簡單,直接封裝了malloc和free處理,增加_malloc_alloc_oom_handle處理機制。二級空間配置器才是stl的精華,二級空間配置器主要由memorypool+freelist構成。

一級空間配置器:

執行流程:

sgi以malloc來配置記憶體。當malloc()失敗後,就呼叫oom_malloc(),如果客戶端沒有設定記憶體不足處理機制,則就直接丟擲bad_alloc異常資訊,或者直接終止程式。如果客戶端設定了記憶體不足處理機制,則他就會一直呼叫記憶體處理機制,企圖在某次呼叫之後獲得一塊足夠的記憶體。但是如果記憶體不足處理機制設計不好的話,存在死迴圈的危險。

二級空間配置器:

二級空間配置器使用記憶體池+自由鍊錶的形式避免了小塊記憶體帶來的碎片化,提高了分配的效率,提高了利用率。sgi的做法是先判斷要開闢的大小是不是大於128,如果大於128則就認為是一塊大塊記憶體,呼叫一級空間配置器直接分配。否則的話就通過記憶體池來分配,假設要分配8個位元組大小的空間,那麼他就會去記憶體池中分配多個8個位元組大小的記憶體塊,將多餘的掛在自由鍊錶上,下一次再需要8個位元組時就去自由鍊錶上取就可以了,如果**這8個位元組的話,直接將它掛在自由鍊錶上就可以了。

為了便於管理,二級空間配置器在分配的時候都是以8的倍數對齊。也就是說二級配置器會將任何小塊記憶體的需求上調到8的倍數處(例如:要7個位元組,會給你分配8個位元組。要9個位元組,會給你16個位元組),儘管這樣做有內碎片的問題,但是對於我們管理來說卻簡單了不少。因為這樣的話只要維護16個free_list就可以了,free_list這16個結點分別管理大小為8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128位元組大小的記憶體塊就行了。

自由鍊錶的結點型別:

[cpp] view plain

copy

"font-size:14px;">union_obj  ;

記憶體池模型:

為了將自由鍊錶下面的結點串起來,又不引入額外的指標,所以我們要學會一物兩用,因為在自由鍊錶下面掛的最小的記憶體塊都是8個位元組,足夠存放乙個位址,所以我們就在這些記憶體塊裡面存放其他記憶體塊的位址,這樣的話就將這些記憶體塊鏈結起來了。

二級空間配置器的邏輯步驟:

假如現在申請n個位元組:

1、判斷n是否大於128,如果大於128則直接呼叫一級空間配置器。如果不大於,則將n上調至8的倍數處,然後再去自由鍊錶中相應的結點下面找,如果該結點下面掛有未使用的記憶體,則摘下來直接返回這塊空間的位址。否則的話我們就要呼叫refill(size_t n)函式去記憶體池中申請。

2、向記憶體池申請的時候可以多申請幾個,stl預設一次申請nobjs=20個,將多餘的掛在自由鍊錶上,這樣能夠提高效率。

進入refill函式後,先調chunk_alloc(size_t n,size_t& nobjs)函式去記憶體池中申請,如果申請成功的話,再回到refill函式。

這時候就有兩種情況,如果nobjs=1的話則表示記憶體池只夠分配乙個,這時候只需要返回這個位址就可以了。否則就表示nobjs大於1,則將多餘的記憶體塊掛到自由鍊錶上。

如果chunk_alloc失敗的話,在他內部有處理機制。

3、進入chunk_alloc(size_t n,size_t& nobjs )向記憶體池申請空間的話有三種情況:

3.1、記憶體池剩餘的空間足夠nobjs*n這麼大的空間,則直接分配好返回就可以了。

3.2、記憶體池剩餘的空間leftalloc的範圍是n<=leftalloc3.3、記憶體池中剩餘的空間連乙個n都不夠了,這時候就要向heap申請記憶體,不過在申請之前先要將記憶體池中剩餘的記憶體掛到自由鍊錶上,之後再向heap申請。

3.3.1、如果申請成功的話,則就再調一次chunk_alloc重新分配。

3.3.2、如果不成功的話,這時候再去自由鍊錶中看看有沒有比n大的空間,如果有就將這塊空間還給記憶體池,然後再調一次chunk_alloc重新分配。

3.3.3、如果沒有的話,則就呼叫一級空間配置器分配,看看記憶體不足處理機制能否處理。

流程框圖:

STL 空間配置器

stl有6大元件 容器 演算法 迭代器 仿函式 配接器 分配器。它們之間的密切關係是stl的精髓所在,容器用來存放資料,而容器存在的前提是要有分配器給它分配記憶體,接下來需要實現演算法,迭代器便作為演算法來對容器資料操作的橋梁,演算法可以使用仿函式完成不同的策略變化,配接器可修飾或套接仿函式。說了麼...

STL 空間配置器

stl空間配置器的底層原理 維護了乙個狹義的記憶體池,並且用乙個自由鍊錶來維護該記憶體池。該自由鍊錶比較類似於雜湊的開鏈法的儲存結構。源 pragma once using namespace std define throw bad alloc cerr out of memory endl ex...

STL空間配置器

一級空間配置器 ifndef malloc alloc template h define malloc alloc template h if 0 include define throw bad alloc throw bad alloc elif defined throw bad alloc...