STL 容器的高效能很大程度仰賴其記憶體配置器。SGI STL 實作的 std::alloc(非標準 allocator,不接受模板參數)採用經典的雙層級配置器設計,巧妙解決小區塊碎片與多執行緒爭用問題。本文結合關鍵原始碼深度剖析其設計思想。
<stl_alloc.h>:負責原始記憶體 allocate/deallocate/reallocate。<stl_construct.h>:負責物件建構/解構 construct/destroy。std::alloc,無需顯式指定:vector 即合法。template
inline void construct(T1* p, const T2& value) {
new (p) T1(value); // placement new:在指定記憶體建構物件
}
p->~T()。destroy(first, last) → __destroy → __destroy_aux。利用 __type_traits 判斷 trivial_destructor,若為真則直接返回(零開銷),否則逐一呼叫單一物件版本。allocate(n) → malloc(n),失敗呼叫 oom_malloc(Out-Of-Memory 處理器)。deallocate(p, n) → free(p)。reallocate(p, old_sz, new_sz) → realloc(p, new_sz),失敗呼叫 oom_realloc。設計細節:因使用 C 的 malloc/realloc,無法直接用 C++ set_new_handler,SGI 自行實作一套 _malloc_alloc_oom_handler 機制模擬 new-handler 行為。
union obj { union obj* free_list_link; char client_data[1]; }; —— 利用「柔性陣列」技巧,同一記憶體區既能串聯 free-list,又能供用戶使用。char* start_free, *end_free,向系統申請大塊(預設 20 × 區塊大小),切分後掛入對應 free-list。n > 128 → malloc_alloc::allocate(n)。n = ROUND_UP(n),找對應 free-list 索引。refill(n) 向記憶體池申請 20 區塊(可調),切分掛入 free-list 後回傳一塊。chunk_alloc 嘗試從系統 malloc 取得大塊,失敗則遍歷較大 free-list 拆分借用,仍失敗則拋 bad_alloc。n > 128 → malloc_alloc::deallocate(p)。n 找 free-list 索引,將 p 插入表頭(LIFO),極快 O(1)。_STL_THREADS 巨集控制:定義時啟用 __malloc_lock/__malloc_unlock(通常映射到 pthread_mutex)。| 優化點 | 機制 | 效益 |
|---|---|---|
| 小區塊碎片 | 16 條 8-byte 對齊 free-list + 記憶體池 | 消除外部碎片,內部碎片 < 8 bytes |
| 管理開銷 | union 節點無額外 header | 100% 記憶體可用於用戶資料 |
| 系統呼叫次數 | 批次向系統申請(預設 20 區塊) | 大幅減少 malloc/free 系統呼叫 |
| Trivial Destructor | 型別特徵判斷跳過解構迴圈 | POD 類型析構零開銷 |
| 記憶體耗盡處理 | OOM handler + 借用較大 free-list | 極限情況下仍嘗試滿足分配 |