內存池出現原因:內存碎片
首先我們需要明確, 內存池的目的到底是什么? 首先你要知道的是, 我們每次使用new T來初始化類型T的時候, 其實發生了兩步操作,
- 一個叫內存分配, 這一步使用的其實不是new而是operator new(也可以認為就是C語言中的malloc), 這一步是直接和操作系統打交道的, 操作系統可能需要經過相對繁瑣的過程才能將一塊指向空閑內存的指針返回給用戶, 所以這也是new比較耗時的一部分,
- 而第二步就是使用構造函數初始化該內存, 這是我們比較熟悉的.
既然內存分配耗時, 那我們很容易想到的就是一次性分配一大塊內存, 然后在用戶需要的時候再划分其中一部分給用戶, 這樣的話, 一次分配, 多次使用, 自然而然提高了效率, 而用來管理這所謂的一大塊內存的數據結構, 也就是今天我們要說的內存池.
另外一個好處在於, 頻繁地使用new將導致系統內存空間碎片化嚴重, 容易導致的后果就是很難找到一塊連續的大塊內存, 造成內存碎片(非連續),空間利用率低.
內碎片是指分配給作業的存儲空間中未被利用的部分,外碎片是指系統中無法利用的小存儲塊。

1.內存申請流程圖
小於等於128k的用第二級分配器;
大於128k的用第一級分配器

2. 第一級配置器:
第一級採用malloc、free;
此外,這個配置器提供了當內存配置錯誤時的處理函數oom*malloc,這個函數會調用*_malloc_alloc_oom_handler()這個錯誤處理函數,去企圖釋放內存,然后重新調用malloc分配內存。如此循環,直到分配成功,返回指針(所以再一定程度上提高內存分配成功)。
3. 第二級配置器
使用自由鏈表(free-list)技巧。主動將不論什么小額區塊的內存需求量上調至8的倍數。如需求30,則上調至32。
free-list節點結構
union obj { union obj * free_list_link; //下一個節點的指針 char client_data[1]; //內存首地址 }
有16個free-lists。各自管理大小分別為8、16、24、32、40、48、56、64、72、80、88、96、104、112、120、128 bytes的小額區塊。

釋放內存

所以最終內存池的思路其實是這樣的:
1. 使用allocate向內存池請求size大小的內存空間, 如果需要請求的內存大小大於128bytes, 直接使用malloc.
2. 如果需要的內存大小小於128bytes, allocate根據size找到最適合的自由鏈表.
a. 如果鏈表不為空, 返回第一個node, 鏈表頭改為第二個node.
b. 如果鏈表為空, 使用blockAlloc請求分配node.
x. 如果內存池中有大於一個node的空間, 分配竟可能多的node(但是最多20個), 將一個node返回, 其他的node添加到鏈表中.
y. 如果內存池只有一個node的空間, 直接返回給用戶.
z. 若果如果連一個node都沒有, 再次向操作系統請求分配內存.
①分配成功, 再次進行b過程
②分配失敗, 循環各個自由鏈表, 尋找空間
I. 找到空間, 再次進行過程b
II. 找不到空間, 拋出異常(代碼中並未給出, 只是給出了注釋)
3. 用戶調用deallocate釋放內存空間, 如果要求釋放的內存空間大於128bytes, 直接調用free.
4. 否則按照其大小找到合適的自由鏈表, 並將其插入.
特點其實是這樣的 :
1. 剛開始初始化內存池的時候, 其實內存池中並沒有內存, 同時所有的自由鏈表都為空鏈表.
2. 只有用戶第一次向內存池請求內存時, 內存池會依次執行上述過程的 1->2->b->z來完成內存池以及鏈表的首次填充, 而此時, 其他未使用鏈表仍然是空的.
3. 所有已經分配的內存在內存池中沒有任何記錄, 釋放與否完全靠程序員自覺.
4. 釋放內存時, 如果大於128bytes, 則直接free, 否則加入相應的自由鏈表中而不是直接返還給操作系統.
參考文章:
https://www.cnblogs.com/zsychanpin/p/6936810.html
https://www.cnblogs.com/nzhl/p/5753728.html
