目的
內存池的作用在於消除頻繁調用系統默認的內存分配和釋放函數所帶來的開銷問題。
由於每次要求分配的內存大小不等,使用默認的內存分配函數的話,可能給系統帶來大量的碎片問題,所以,將內存配置問題交給底層的內存池去處理,是一個不錯的選擇。
設計
本來打算自己實現一個內存池,想了想還是算了。總結這篇文章的目的在於深入剖析內存池相關內容,主要是相關思想,而不在於代碼實現上。所以,通過STL的底層空間配置器來完成本文。
一點思考
考慮一個常規的空間配置器(或者說內存池)的作用是什么?分配內存和釋放內存。
使用了空間配置器后,我們將函數調用malloc/free、new/delete的工作包裝到配置器中,然后內存池提供相關的分配內存和釋放內存接口。這就是最簡單的空間配置器
內存池細節考慮
由於每一次分配內存的大小不定,所以考慮到管理方便,STL內存池采用兩級內存配置器,若要求的內存大於128bytes,使用malloc/free直接分配和回收。若小於等於128bytes時,由內存池分配和回收。
在內存池中,以8bytes為單位,即維護從8,16,24,..,128bytes的16個鏈表,每個鏈表上鏈接的是對應大小的區塊。每一次分配內存時,先將要求的內存大小擴展為8的倍數,然后從對應鏈表上取走一個區塊,釋放時,將區塊重新鏈接到對應鏈表上。
內存池接口
//內存池
class Alloc{
public:[enter description here][1]
static void *allocate(size_t n); //分配指定大小的內存
static void deallocate(void *p, size_t n); //回收指定內存
static void *reallocate(void *p, size_t old_sz, size_t new_sz); //重新分配內存
private:
union obj{ //free-lists節點構造
union obj *free_list_link;
char client_data[1];
};
private:
//16個free-lists
static obj * volatile free_list[NUMFREELISTS];
//以下函數根據區塊的大小,決定使用第n號free-lists. n從0算起
static size_t FREELIST_INDEX(size_t bytes){
return ((bytes+ALIGN - 1) / ALIGN - 1);
}
//將bytes上調至8的倍數
static size_t ROUND_UP(size_t bytes){
return (((bytes)+ALIGN - 1) & ~(ALIGN - 1));
}
//返回一個大小為n的對象,並可能加入大小為n的其他區塊到free-list
static void *refill(size_t n);
//配置一大塊空間,可容納nobjs個大小為size的區塊
//如果配飾nobjs個區塊無法滿足,nobjs可能會降低
static char *chunk_alloc(size_t size, int &nobjs);
//區塊狀態
static char* start_free; //內存池起始位置
static char* end_free; //內存池結束位置
static size_t heap_size;
};
-
誠如我前面所講,public接口只有三個,分配內存,釋放內存,重新分配內存。具體的實現過程在此不做列出,有興趣的參考列出的源碼
-
在STL的空間配置器中使用了兩層配置方法。當需要的內存大於128bytes,則直接使用malloc函數分配,即第一層配置器只是對系統函數一個簡單的包裝。
總結
-
仿STL底層空間配置器實現了一個內存池,並給出了一個測試用例。見MemoryPool
-
池的概念,需要好好體會。