new/malloc 每次分配需存儲塊大小、狀態等元數據(通常 8-16 字節),並對齊到 8/16 字節。對 12 字節雙向鏈表節點,實際佔用 24 字節,**開銷 100%**。嵌入式/高併發場景不可接受。
single_object_allocator 封裝)。// Chunk: 64 塊 + 1 個位圖 (uint64_t)
struct Chunk {
uint64_t map; // 位圖:1=空閒, 0=已用
Object objects[64]; // 緊湊排布
};
// Object: 1 字節索引 + 用戶數據
union Object { uint8_t idx; char pad[1]; };
Chunk 總大小:8 + 64 * (BlockSize + 1) 字節。僅保留一個部分使用的 Chunk 快取,其余滿/空 Chunk 即時歸還 OS。
find_fbit(map) 找首個 1 位 → mark_bit(map, idx) 置 0 → 返回物件指標。chunk_alloc() 向 OS 申請新 Chunk,初始化位圖全 1,遞歸分配。
Object* obj = ptr - 1; idx = obj->idx;
2. Chunk* chk = (Chunk*)((char*)obj - idx * blk_sz) - 1;
3. clear_bit(chk->map, idx) 置 1。
4. 若 chk->map == FULL (all 1) 且非快取 Chunk → free(chk) 歸還 OS。
bsf (Bit Scan Forward) 指令,極快。lsb_64_table),O(1) 無分支。class bitmap_fixed_size_allocator : public default_mutex {
using size_type = uint64_t;
using index_type = uint8_t;
static Chunk* cached_chunk; // 單快取 Chunk
size_type blk_sz; // 含索引的塊大小
size_type sz_lmt; // 閾值:超過此大小直接 malloc
void* allocate() {
if (blk_sz > sz_lmt) return malloc(blk_sz - sizeof(Object));
Chunk* c = cached_chunk;
if (!c || c->map == 0) return chunk_alloc(); // 滿或無快取
return get_free_block(c);
}
void deallocate(void* p) { /* 反向計算 chunk/idx, 置位, 檢查全空歸還 */ }
// ... 位圖操作、chunk_alloc、find_fbit 等 ...
};
template<typename T>
class single_object_allocator {
static bitmap_fixed_size_allocator bm;
public:
using value_type = T;
T* allocate(size_t n) {
if (n != 1) throw; // 僅支援單個物件
return static_cast<T*>(bm.allocate());
}
void deallocate(T* p, size_t n) {
if (n != 1) { bm.clear(); throw; }
bm.deallocate(p);
}
// construct/destroy/max_size/rebind 等標準介面...
};
| 指標 | new/malloc | Bitmap Allocator |
|---|---|---|
| 1M 節點 (12B) 總記憶體 | ~24 MB | ~13 MB (含索引開銷) |
| 單塊額外開銷 | 8-16 B | 1 B |
| 分配/釋放速度 | 基準 | 略快 (無鎖快取情況) |
| 記憶體碎片 | 較高 | 極低 (固定塊) |
std::list 等標準容器(需修改 allocate 接受 size_t)。位圖固定大小分配器以 **1 字節/塊** 的極致元數據開銷,配合 **64 塊/Chunk** 的緊湊佈局與 **LSB 位掃描**,實現了接近理論下限的小物件記憶體管理。理解其設計權衡(開銷 vs 通用性、快取策略 vs 碎片),可指導自定義記憶體池開發。