1. 程式人生 > >C++ STL 記憶體分配的思想以及使用union(共用體)的妙處

C++ STL 記憶體分配的思想以及使用union(共用體)的妙處

STL空間配置器

前言:

今天看書《STL原始碼剖析》,書中說道:空間配置器;空間配置器是用來為容器分配記憶體的一個東西,空間配置器中有兩種配置器:第一級配置器,第二級配置器

當用戶申請的記憶體小於128bytes的時候,用第二級配置器,當用戶申請的記憶體大於128bytes的時候,第二級配置器已經不能滿足,這個時候要祭出第一級配置器這個武器。

第一級配置器

第一級配置器直接呼叫malloc函式來分配記憶體。

第二級配置器

free_list結構:

第二級配置器才是重點:為了解決分配記憶體的時候出現碎片的問題,第二級配置器使用了記憶體池的機制,首先,free_list 維護了16個連結串列,每個連結串列my_free_list 存放的節點的大小是~~~這一類同樣大小的記憶體(一般是20個),最小的是8個位元組,然後開始以8的倍數遞增,分別是 8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128,perfect  !!!,剛好到128個位元組,這就是為什麼大於128bytes要使用第一級配置器了,因為,第二級配置器最多隻能分配128bytes,

好了,這16個連結串列,裡邊每一個連結串列的節點都是這種結構

union obj{
        union obj *free_list_link;
        char client_data[1];
}

free_list分配:

如果使用者要申請大小為N的記憶體,如果N>128,用第一級配置器,如果N<128,假設N=15,為了避免記憶體碎片,把N上調到8的倍數,這裡是16,那麼就去找到大小為節點16的那個空閒連結串列,my_free_list = free_list + FREELIST_INDEX(N),     FREELIST_INDEX(N),的功能是把通過N上調到合適的8的倍數,然後算出相應的那一個free_list的號數,比如說:N = 15 那就調到16,16/8 = 2,從0開始算那就是第一號free_list,,如果那個連結串列free_list 有可用的記憶體塊,那麼就把第一塊拿出來,然後就把頭指標往下移動一個單位 ,比如說找的了連結串列指標是my_free_list,取出第一個 result = * my_free_list ,   連結串列頭指標下移my_free_list = result ->free_list_link;

free_list無可用,memory pool 登場之1:

如果第二號free_list的記憶體塊都用完了,這個時候,向記憶體池申請,一般是20個大小為16bytes的記憶體,把第一個返回,其餘19個加到free_list中

free_list無可用,memory pool 登場之2:

在退一步,如果我們的大本營memory pool,記憶體池不夠提供20個大小為16的塊,但是足夠提供幾個可能是7個,那麼就把這7個給分配出去1個返回,6個加到free_list,注意這個時候記憶體池已經空了

free_list無可用,memory pool 登場之3:


再慘一點,如果記憶體池裡邊只有一個,那就直接返回這個,如果記憶體池裡邊的大小不足以提供一個申請的單位,如不足16bytes,那麼也不要浪費,把剩下來的一點點進行判斷 是否為8的倍數,分出去加到相應的free_list裡邊,這個時候就要開始使用malloc來分配heap記憶體了,這次申請的標準也定的挺巧妙,是 (40+n)*16 ,因為free_list向記憶體池申請20 * 16 bytes,那麼我就申請雙倍一半給free_list ,一半給memory pool,至於n嘛,為1個附加值,你申請的次數越多,n就越大,這更符合實際

感嘆:

好了,以上就是記憶體分配的全部過程,比較複雜,不過真心巧妙,宛如一件藝術品。

釋放:

如果要釋放記憶體的話,很簡單,若釋放的大小size>128bytes,直接呼叫第一級配置器的釋放函式dealloc(),其實就是內部呼叫free(),如果釋放的大小<128那麼判斷大小從而確定 屬於哪一號free_list,比如說是64,那就是64/8 -1,那就是第7號free_list,把這塊要回收的記憶體插到第7號free的開頭,然後指標my_free_list指標向前移動一個節點,ok!!!

這次真的領略到了大師的水準啊,這個記憶體分配回收機制太美妙了,極大的減少了開闢空間回收空間的損耗,不到迫不得已並不會使用malloc(),對於記憶體的利用無所不用其法,佩服佩服!!

附加:union的妙用

另外:free_list的節點的定義也是非常的巧妙!!

union 修飾的這種資料型別,裡面可以存放多種不同型別的資料,並且公用一塊記憶體,也就是說   free_list_link  和client_data 用的是同一塊記憶體,前者表示指向下一個節點的指標,後者表示一個指向實際記憶體的指標,它們公用同一塊記憶體,正是因為無論在什麼情況下,二者只用其一,情況無非兩種,分配和不分配,不分配的時候,節點就放在空閒連結串列裡面,節點內容是下一個節點的地址,如果被分配了,那麼節點內容就是指向一塊實際的記憶體,這樣就不用在節點裡開兩個不同記憶體的變數,節省的好多空間(正正符合STL的哲學),這個做法太完美了!!!!

union 主要用於這種 n選1的情況,也可以用例外一種情況以達到例外一種妙用,

union Matrix
{
       struct m{_11;_12,_13;_21,_22;_23;_31;_32;_33};
       int a[3][3];
}

3D中經常用到的Matrix 結構體,一個元素可以有兩種,表示方式,_11,和a[0][0];符合不同的人的使用習慣