自訂記憶體配置器

pool/固定大小區塊配置器

想像一塊插板,上面有一百個一模一樣的插孔,用來放一百根一模一樣的插銷。發一根插銷很簡單——抓任一個空孔的插銷。歸還一根也很簡單——丟進任一個空孔。因為每根插銷、每個孔都一樣大,你永遠不必量尺寸、不必搜尋、也不必擔心合不合。pool 配置器對記憶體就是這麼運作:它管理許多大小全部相同的固定區塊,於是配置與釋放各自只剩一次指標移動。

其構造如下。你事先決定一個區塊大小——比如 sizeof(struct Particle)、64 位元組——把一大塊記憶體切成那種固定大小槽位的陣列。空閒槽位被串成一條自由串列,每個空閒槽位的最前面幾個位元組存著指向下一個空閒槽位的指標(又是內嵌自由串列指標的技巧,因為空閒槽位沒在用所以安全)。要配置,你取下自由串列的鏈首:拿走第一個空閒槽位,把串列鏈首設成該槽位的 next 指標,完成。要釋放,你推入:把目前的鏈首寫進被釋放槽位的最前面幾個位元組,讓它成為新的鏈首。兩個操作都是 O(1),沒有大小引數、沒有湊整、沒有適配搜尋、也沒有碎片——每個槽位都可互換,所以一個被釋放的槽位完美吻合下一個請求。

當程式大量建立與銷毀某一種特定型別的物件時,pool 就是標準工具:遊戲裡的粒子、伺服器裡的連線記錄、樹裡的節點。它們快、可預測(常數時間,無最壞情況搜尋),且因所有區塊相同而沒有碎片。誠實的限制:一個 pool 只服務一種大小,所以你通常會為不同物件型別跑好幾個 pool;而單一個有 N 個槽位的 pool 最多只能容納 N 個物件——你必須估算其大小,或在它滿時串接更多 slab。

typedef struct Slot { struct Slot *next; } Slot; Slot *free_head; /* 自由串列的鏈首 */ void *pool_alloc(void) { if (!free_head) return NULL; Slot *s = free_head; /* 取下鏈首…… */ free_head = s->next; /* ……O(1),無搜尋 */ return s; } void pool_free(void *p) { Slot *s = p; s->next = free_head; /* 推回去,O(1) */ free_head = s; }

每個槽位大小相同,所以配置與釋放各只是一次串列取出與推入——無適配搜尋、無碎片。

pool 在建構時就把大小固定;向它要不同大小它就幫不上忙。這是刻意的取捨:放棄通用性,換取對單一物件型別的常數時間、無碎片服務。

又称
object allocatorfixed-size allocatorfree-list pool物件配置器固定大小配置器記憶體池