arena/region(指標推進)配置器
想像一長卷收據紙和一枝筆。每次你要記點東西,就在目前位置寫下,再把筆往前滑動你所寫的那麼多。你從不回頭去擦掉單一筆記錄——根本沒有橡皮擦。整件工作做完時,你把整卷紙一次扔掉。這就是 arena 配置器:配置就只是把一個指標往前移(一次推進,bump),而釋放是把全部一筆勾銷。沒有逐物件的釋放。
它的機制簡單到近乎好笑。arena 持有一個大緩衝區與單一個指標,常稱為推進指標(bump pointer),標示使用中與空閒之間的界線。要配置 n 個位元組,你把 n 向上湊到對齊邊界、檢查還有沒有空間、回傳目前的指標,再把它前進 n。就這樣——配置只是幾條指令,沒有搜尋、沒有自由串列、沒有標頭。代價是沒有個別釋放:free(p) 不做任何有意義的事,因為沒有東西追蹤 p 在哪裡結束、或它之後是否還有人存活。回收只能靠重置整個 arena:把推進指標移回起點(或釋放緩衝區)。那一瞬間,arena 裡的每個物件同時消失。
這非常適合以階段為單位、許多物件共享同一生命期的工作:剖析一個檔案(配置所有 AST 節點、處理它們、釋放 arena)、服務一個網頁請求(為該請求配置一切,回應送出後全部丟棄)、或一個遊戲畫格。它極快,幾乎沒有任何逐物件的記帳開銷,並完全迴避了個別物件的碎片與釋放後使用。誠實的限制:當物件擁有各自獨立、交錯的生命期時它就沒用——若 arena 中某些物件必須比其他物件活得久,你就無法在最後一個結束前回收記憶體,於是一個長壽物件會把整塊區域釘住。
typedef struct { char *buf; size_t used, cap; } Arena; void *arena_alloc(Arena *a, size_t n) { n = (n + 15) & ~(size_t)15; /* 向上湊到 16 位元組對齊 */ if (a->used + n > a->cap) return NULL; void *p = a->buf + a->used; /* 交出目前位置 */ a->used += n; /* 把指標往前推進 */ return p; } void arena_reset(Arena *a) { a->used = 0; } /* 一次釋放「全部」 */
配置就只是推進指標;沒有逐物件釋放,只有整個 arena 的重置。
arena 以放棄個別回收換取速度:你無法提早釋放單一物件。它只在一批物件真正共享同一生命期時才理想;摻入長壽物件就會破壞它的優勢。