動態記憶體配置 (Dynamic Memory Allocation)
Overview Table
| 小節 | 主題 | 核心重點 |
|---|---|---|
| 9.9 | 動態配置器概觀 (p.875) | heap 由 allocator 管理;explicit(malloc/free)vs implicit(GC)兩種風格 |
| 9.9.1 | malloc / free / sbrk (p.876) | malloc 回傳對齊的至少 size bytes;free 參數必須來自 malloc/calloc/realloc |
| 9.9.2 | 為何要動態配置 (p.879) | 資料結構大小 run time 才知道;硬編上限是維護惡夢 |
| 9.9.3 | 需求與目標 (p.880) | 立即回應、只用 heap、對齊、不可搬動已配置區塊;最大化 throughput 與 peak utilization |
| 9.9.4 | 碎片化 (p.882) | internal(block > payload)vs external(總量夠但無單一夠大 free block) |
| 9.9.5 | 實作議題 (p.882) | free block 組織、placement、splitting、coalescing 四大問題 |
| 9.9.6 | Implicit free list (p.883) | header 編碼 size+allocated bit;靠 size 欄位「隱式」串起所有 block |
| 9.9.7 | Placement (p.885) | first fit / next fit / best fit 的取捨 |
| 9.9.8 | Splitting (p.885) | 整塊用掉(內部碎片)或切割成 allocated + 新 free block |
| 9.9.9 | 擴充 heap (p.886) | 先 coalesce,不夠再 sbrk 要新記憶體 |
| 9.9.10 | Coalescing (p.886) | 對抗 false fragmentation;immediate vs deferred |
| 9.9.11 | Boundary tags (p.887) | footer = header 複本 → 常數時間合併前一塊;四種 case |
| 9.9.12 | 簡單配置器實作 (p.890) | implicit list + prologue/epilogue + 立即 boundary-tag coalescing |
| 9.9.13 | Explicit free list (p.898) | free block 內藏 pred/succ 指標;LIFO vs address order |
| 9.9.14 | Segregated free lists (p.899) | size class 陣列;simple segregated storage、segregated fits、buddy system |
9.9 動態記憶體配置器與 Heap (p.875)
雖然可直接用 mmap/munmap 建立虛擬記憶體區域,C 程式設計者通常用動態記憶體配置器 (dynamic memory allocator) 取得 run-time 記憶體,較方便也較可攜。配置器維護稱為 heap 的虛擬記憶體區域:demand-zero、緊接在 .bss 之後、向高位址成長;kernel 為每個 process 維護指向 heap 頂端的 brk 指標。
高位址
+---------------------------+
| User stack | 向下成長
+---------------------------+
| ... |
+---------------------------+
| Memory-mapped region |
| (shared libraries) |
+---------------------------+
| ... |
+---------------------------+ <-- brk (top of the heap)
| Heap | 向上成長
+---------------------------+
| Uninitialized data (.bss) |
+---------------------------+
| Initialized data (.data) |
+---------------------------+
| Code (.text) |
+---------------------------+
低位址 (0)
- heap 是各種大小 block 的集合;每個 block 是連續的虛擬記憶體 chunk,狀態為 allocated 或 free。
- Explicit allocator:應用程式自己 free(C 的 malloc package;C++ 的
new/delete)。 - Implicit allocator(= garbage collector):由配置器偵測不再使用的 block 並自動釋放(Lisp、ML、Java);見 09-Virtual-Memory/06-Garbage-Collection-and-Memory-Bugs。
記憶體配置是通用概念:圖形演算法等應用常先向標準 allocator 要一大塊,再用 application-specific allocator 自行管理其中的節點 (p.876)。
9.9.1 malloc 與 free (p.876)
#include <stdlib.h>
void *malloc(size_t size); // 成功回傳 block 指標,失敗回傳 NULL 並設 errno
void free(void *ptr); // 無回傳值
#include <unistd.h>
void *sbrk(intptr_t incr); // 成功回傳舊 brk,失敗回傳 -1 (errno=ENOMEM)
malloc回傳至少 size bytes 且對齊到可容納任何資料型別的 block:32-bit mode 位址為 8 的倍數,64-bit mode 為 16 的倍數 (p.877)。malloc不初始化記憶體;要清零用 calloc(malloc 的薄包裝),要改變已配置 block 大小用 realloc。sbrk(incr)把 kernel 的 brk 加上 incr 以伸縮 heap;incr == 0回傳目前 brk;負的 incr 合法但棘手(回傳值指向新頂端之後abs(incr)bytes 處)。free(ptr)的ptr必須指向由 malloc/calloc/realloc 取得之 block 的開頭,否則行為未定義;且 free 無回傳值,不會告知任何錯誤 (p.877)。- 本節術語:word = 4 bytes、double word = 8 bytes(與 Intel 把 4 bytes 叫 double word 不同,p.877 Aside)。
Figure 9.34 (p.878) 以 16-word 小 heap 示範:malloc 從 free block 前端切塊;請求 5 words 時 malloc 實際配 6 words(padding 一個 word 維持 double-word 對齊);free 後指標 p2 仍指向舊處——應用程式有責任不再使用它。
9.9.2 為何需要動態配置 (p.879)
程式常在 run time 才知道資料結構大小。用 #define MAXN 15213 硬編陣列上限:上限與實際可用虛擬記憶體無關、超過就得重新編譯,在百萬行等級的軟體中是維護惡夢。改用 array = (int *)Malloc(n * sizeof(int)) 在讀到 n 之後才配置,上限僅受可用虛擬記憶體限制。
9.9.3 配置器的限制與目標 (p.880)
Explicit allocator 的硬性限制:
| 限制 | 意義 |
|---|---|
| 處理任意 request 序列 | 不可假設 allocate/free 成對或巢狀;free 必須對應先前配置且目前 allocated 的 block |
| 立即回應 | 不可為了效能而重排或緩衝 requests |
| 只使用 heap | 配置器自身的非 scalar 資料結構也必須存在 heap 內 |
| 對齊 blocks | block 必須能容納任何型別的資料物件 |
| 不修改已配置 blocks | 只能操作 free blocks;不允許 compaction(搬動已配置 block) |
兩個互相衝突的目標 (p.881):
- Goal 1 — 最大化 throughput:單位時間完成的 request 數(如 1 秒完成 500 allocate + 500 free = 1,000 ops/s)。合理目標:allocate 最壞情形與 free block 數成線性、free 為常數時間。
- Goal 2 — 最大化 memory utilization:虛擬記憶體受 disk 上 swap space 限制,並非無限。以 peak utilization 度量:request
完成後, = 目前所有 allocated blocks 的 payload 總和(aggregate payload), = 目前 heap 大小(單調不減):
目標是最大化整個序列的
9.9.4 碎片化 (Fragmentation) (p.882)
Fragmentation = 未使用的記憶體無法滿足 allocate requests,是 heap 利用率差的主因。
| Internal fragmentation | External fragmentation | |
|---|---|---|
| 定義 | allocated block 比 payload 大 | 總 free 記憶體足夠,但無單一 free block 夠大 |
| 成因 | 最小 block size 限制、對齊 padding | free 空間被切碎散佈在多個 blocks |
| 量化 | 容易: |
困難:還取決於未來 requests 的模式,無法預測 |
| 對策 | 好的 placement / 精簡 block 格式 | 配置器以 heuristics 維持「少量大 free blocks」而非「大量小 free blocks」 |
9.9.5 實作議題 (p.882)
Naive allocator:heap 當大 byte 陣列,malloc 就遞增指標 p,free 什麼都不做——throughput 極佳但從不重用 blocks,utilization 極差。實用配置器必須處理四個議題:
- Free block organization:如何追蹤 free blocks?
- Placement:選哪個 free block 放新配置的 block?
- Splitting:放進去之後,剩餘部分怎麼辦?
- Coalescing:block 剛被 free 之後怎麼辦?
9.9.6 Implicit Free List (p.883)
block 格式:1-word header + payload + 選擇性 padding。header 編碼 block size(含 header 與 padding)與 allocated bit。double-word 對齊下 size 是 8 的倍數 → 低 3 bits 恆為 0,只需存高 29 bits,低 3 bits 可編碼其他資訊(最低位 = allocated bit,a=1 allocated、a=0 free):
31 3 2 1 0
+------------------------+-----+
| Block size |0 0 a| Header (1 word)
+------------------------+-----+
| Payload | malloc 回傳指向此處的指標
| (allocated block only) |
+------------------------------+
| Padding (optional) |
+------------------------------+
header 編碼公式:header = size | alloc_bit
- allocated、size 24 (0x18):
0x00000018 | 0x1 = 0x00000019 - free、size 40 (0x28):
0x00000028 | 0x0 = 0x00000028
heap 組織成連續的 allocated/free blocks 序列,free blocks 由 header 的 size 欄位隱式串連——走訪所有 blocks 即可間接走訪所有 free blocks:
Start 終止 header
of | unused | 8/0 | 16/1 | 32/0 | 16/1 | 0/1 |
heap padding free alloc free alloc epilogue(size 0, a=1)
(標記為 size/allocated bit)
- 需要特殊的結尾 block:size 0、allocated bit 設 1 的 terminating header(設 allocated 簡化 coalescing)。
- 優點:簡單。缺點:任何需要搜尋 free list 的操作(如 placement)都與 heap 中 allocated + free 總 block 數成線性。
- 對齊要求 + block 格式 ⇒ 最小 block size:double-word 對齊下每個 block 必為 8 bytes 的倍數,此格式的最小 block = 2 words(1 word header + 1 word 維持對齊)——即使只 malloc 1 byte 也配 2 words (p.884)。
Practice 9.6 (p.885) 模式:size 向上取整公式(此格式,header 1 word、無 footer)= round_up(payload + 4, 8)。例:malloc(2)→8 (0x9)、malloc(9)→16 (0x11)、malloc(15)→24 (0x19)、malloc(20)→24 (0x19)。
9.9.7 放置策略 (Placement) (p.885)
應用程式要求 k bytes 時,配置器依 placement policy 搜尋夠大的 free block:
| 策略 | 作法 | 優點 | 缺點 |
|---|---|---|---|
| First fit | 從 list 開頭找第一個能容納者 | 傾向把大 free blocks 留在 list 尾端 | 開頭累積小碎片「splinters」,拉長大 block 的搜尋時間 |
| Next fit (Knuth 提出) | 從上次搜尋停止處繼續 | 可比 first fit 快很多(尤其開頭多 splinters 時) | 研究顯示 utilization 比 first fit 差 |
| Best fit | 檢查每個 free block,選最小的能容納者 | utilization 通常最佳 | 在 implicit list 等簡單組織上需徹底搜尋 heap |
Segregated free list(9.9.14)可不需徹底搜尋就近似 best fit。
9.9.8 分割 Free Blocks (Splitting) (p.885)
找到 fit 之後的政策決定:
- 整塊使用:簡單快速,但造成 internal fragmentation;若 placement 產生的 fit 很好,尚可接受。
- 分割 (split):fit 不佳時,把 free block 切成兩半——前半成為 allocated block,剩餘部分成為新的 free block(如 Figure 9.37:8-word free block 滿足 3-word 請求後切成 16/1 + 16/0)。
9.9.9 取得額外 Heap 記憶體 (p.886)
找不到 fit 時的流程:
找不到 fit
|
v
合併 (coalesce) 實體相鄰的 free blocks 造出更大 free block
|
+-- 仍不夠大 / 已最大程度合併?
|
v
呼叫 sbrk 向 kernel 要額外 heap 記憶體
|
v
把新記憶體轉成一個大 free block,插入 free list,
再把 requested block 放進這個新 free block
9.9.10 合併 Free Blocks (Coalescing) (p.886)
free 一個 block 後,相鄰可能已有其他 free blocks,造成 false fragmentation:可用記憶體被切成許多小而無用的 free blocks(如兩個相鄰 3-word-payload free blocks 無法滿足 4-word 請求,即使總量足夠)。實用配置器必須合併相鄰 free blocks。
- Immediate coalescing:每次 free 就合併相鄰者——直觀、常數時間;但某些 request 模式會thrashing(同一 block 反覆合併又分割)。
- Deferred coalescing:延後到某時機(如配置失敗時掃描整個 heap 一次合併)。快的配置器常採用某種 deferred coalescing;本書討論假設 immediate coalescing。
9.9.11 Boundary Tags (p.887)
合併下一個 block 容易:current header 走到 next header,檢查 allocated bit,若 free 就把 size 加進 current header,常數時間。合併前一個 block 在只有 header 的 implicit list 中得線性搜尋整個 list。
Knuth 的 boundary tags:在每個 block 尾端加 footer(header 的複本)。前一個 block 的 footer 永遠位於 current block 開頭往前一個 word 處 → 常數時間得知前一 block 的起點與狀態。
+------------------------+-----+
| Block size | a/f | Header
+------------------------+-----+
| Payload |
+------------------------------+
| Padding (optional) |
+------------------------------+
| Block size | a/f | Footer (boundary tag, header 複本)
+------------------------------+
free 當前 block(n)時的四種 case(皆常數時間):
| Case | prev | next | 動作 |
|---|---|---|---|
| 1 | allocated | allocated | 不合併,只把 current 標成 free |
| 2 | allocated | free | 合併 current+next:更新 current 的 header 與 next 的 footer 為 n+m2 |
| 3 | free | allocated | 合併 prev+current:更新 prev 的 header 與 current 的 footer 為 n+m1 |
| 4 | free | free | 三塊全合併:更新 prev 的 header 與 next 的 footer 為 n+m1+m2 |
省 footer 的最佳化 (p.888):footer 的 size 欄位只在前一 block 是 free 時才需要。把「前一 block 的 allocated/free bit」存入 current header 多餘的低位 bit,則 allocated blocks 不需 footer(空間可作 payload);free blocks 仍需 footer。這在應用程式操作大量小 blocks 時很重要——否則 header+footer 可能吃掉小 block 的一半空間。
Practice 9.7 (p.888) 最小 block size(header/footer 各 4 bytes、payload 不可為 0):
| 對齊 | Allocated block 格式 | Free block 格式 | 最小 block size |
|---|---|---|---|
| Single word (4B) | header + footer | header + footer | 12 bytes |
| Single word (4B) | 只有 header | header + footer | 8 bytes |
| Double word (8B) | header + footer | header + footer | 16 bytes |
| Double word (8B) | 只有 header | header + footer | 16 bytes |
9.9.12 實作簡單配置器 (p.890)
以 implicit free list + immediate boundary-tag coalescing 實作;最大 block size gcc -m32/-m64 皆可)。
memlib.c 記憶體模型 (p.890):mem_init 用大陣列模擬 heap(避免干擾系統 malloc);mem_sbrk 介面/語意同 sbrk 但拒絕縮小 heap。配置器 (mm.c) 匯出三個函式:mm_init(成功回傳 0)、mm_malloc、mm_free(語意同系統版本)。最小 block size = 16 bytes。
Heap 不變形式 (invariant form) (p.891, Figure 9.42):
Prologue Regular Regular Regular Epilogue
block block 1 block 2 ... block n block
+------+----+----+---------+----------+-----+---------+-----+
|unused| 8/1| 8/1|hdr .. ftr|hdr .. ftr| ... |hdr .. ftr| 0/1 |
+------+----+----+---------+----------+-----+---------+-----+
pad hdr ftr ^
(prologue) heap_listp
- 開頭 1 個 unused padding word(維持 double-word 對齊)。
- Prologue block:8-byte allocated block,只有 header+footer,初始化時建立、永不 free。
- Epilogue block:size 0 的 allocated block,只有 header。
- prologue/epilogue 是消除 coalescing 邊界條件的技巧——否則每次 free 都要檢查 block 是否在 heap 開頭/結尾,程式碼更亂、更易錯、更慢。
- 私有全域變數
heap_listp恆指向 prologue block。
常數與巨集 (Figure 9.43, p.893)——考試常考的指標運算:
#define WSIZE 4 /* word / header / footer 大小 */
#define DSIZE 8 /* double word */
#define CHUNKSIZE (1<<12) /* 每次擴充 heap 的預設量 = 4 KB */
#define PACK(size, alloc) ((size) | (alloc))
#define GET(p) (*(unsigned int *)(p))
#define PUT(p, val) (*(unsigned int *)(p) = (val))
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
#define HDRP(bp) ((char *)(bp) - WSIZE)
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))
bp(block pointer)指向第一個 payload byte;巨集可組合,如 GET_SIZE(HDRP(NEXT_BLKP(bp))) 取得下一 block 的 size。
mm_init (p.894):mem_sbrk(4*WSIZE) 取 4 words → 依序寫入 padding、prologue header PACK(DSIZE,1)、prologue footer、epilogue header PACK(0,1);heap_listp += 2*WSIZE;再 extend_heap(CHUNKSIZE/WSIZE) 建立初始 free block。
extend_heap (p.894):在 (1) heap 初始化與 (2) mm_malloc 找不到 fit 時被呼叫。把 words 向上取整為偶數(size = (words%2) ? (words+1)*WSIZE : words*WSIZE)維持對齊;mem_sbrk 回傳的 chunk 緊接在舊 epilogue header 之後——舊 epilogue header 變成新 free block 的 header,chunk 最後一個 word 寫入新 epilogue header;最後呼叫 coalesce(bp)(舊 heap 很可能以 free block 結尾)。
mm_free 與 coalesce (p.896):mm_free 把 header/footer 的 alloc bit 清 0 後呼叫 coalesce;coalesce 依 GET_ALLOC(FTRP(PREV_BLKP(bp))) 與 GET_ALLOC(HDRP(NEXT_BLKP(bp))) 分四個 case 實作 Figure 9.40,case 3/4 最後 bp = PREV_BLKP(bp)。
mm_malloc (p.897):
size == 0的 spurious request 回傳 NULL。- 調整 block size(adjusted size,含 header/footer overhead 與對齊):
if (size <= DSIZE)
asize = 2*DSIZE; /* 最小 16 bytes */
else
asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); /* 加 overhead 後向上取整到 8 的倍數 */
find_fit(asize)搜尋 free list;找到就place(bp, asize)(放置並視情況分割)。- 找不到則
extendsize = MAX(asize, CHUNKSIZE),extend_heap(extendsize/WSIZE)後再 place。
Practice 9.8/9.9 (p.897):find_fit 為 implicit list 的 first-fit 搜尋;place 把 block 放在 free block 開頭,只有剩餘部分 ≥ 最小 block size (16 bytes) 才分割。
9.9.13 Explicit Free Lists (p.898)
implicit list 的配置時間與總 block 數成線性,不適合通用配置器(僅適合 block 數已知很小的特殊用途)。改為把 free blocks 組成顯式資料結構:free block 的 body 程式用不到,可存放指標——如在每個 free block 內放 pred / succ 指標構成雙向鏈結 free list:
Allocated block Free block
+--------------+-----+ +--------------+-----+
| Block size | a/f | | Block size | a/f | Header
+--------------------+ +--------------------+
| | | pred (predecessor) |
| Payload | +--------------------+
| | | succ (successor) |
+--------------------+ +--------------------+
| Padding (optional) | | (old payload) |
+--------------------+ +--------------------+
| Block size | a/f | | Block size | a/f | Footer
+--------------+-----+ +--------------+-----+
- first-fit 配置時間:從「總 block 數線性」降為「free block 數線性」。
- free 的時間取決於 list 的排序政策:
| 排序政策 | free 時間 | 特性 |
|---|---|---|
| LIFO(新 free 的插到 list 開頭) | 常數時間(搭配 boundary tags 則 coalescing 也是常數) | first fit 會先檢查最近使用過的 blocks |
| Address order(list 中位址遞增) | 線性(需搜尋找 predecessor) | address-ordered first fit 的 utilization 較佳,接近 best fit |
- 缺點:free block 必須裝得下所有指標 + header (+ footer) → 最小 block size 變大,增加 internal fragmentation 的可能。
9.9.14 Segregated Free Lists (p.899)
Segregated storage:維護多條 free lists,每條放大小相近的 blocks——把所有可能的 block size 分割成 size classes(等價類)。例如以 2 的冪分割:
或小 blocks 各自成類、大 blocks 以 2 的冪分割。配置器維護一個 free list 陣列(依 size 遞增排列);要 size n 的 block 時搜尋對應的 list,不夠就搜尋下一個更大的 class。
Simple Segregated Storage (p.900)
每個 size class 的 free list 只含同一大小的 blocks(= 該 class 的最大元素;如 class {17–32} 的 list 全是 32-byte blocks)。
- 配置:對應 list 非空 → 直接拿第一個 block 整塊使用(從不 split);list 空 → 向 OS 要固定大小 chunk(通常是 page size 倍數),切成等大 blocks 串成新 list。
- 釋放:直接把 block 插回對應 list 開頭(從不 coalesce)。
- 優點:allocate/free 皆快速常數時間;同 chunk 內 blocks 等大 → 由位址即可推得 size;不 coalesce → allocated block 不需 header 也不需 footer;list 只在頭端插刪 → 單向鏈結即可。唯一必要欄位是 free block 內 1 word 的 succ 指標 ⇒ 最小 block size = 1 word。
- 缺點:易受 internal fragmentation(不 split)與極端 external fragmentation(不 coalesce;Practice 9.10:反覆 malloc/free 交替 pattern 會讓某 size class 累積大量永不重用的 free blocks)。
Segregated Fits (p.900)
每條 free list 對應一個 size class,內含該 class 中各種大小的 blocks(explicit 或 implicit list)。
- 配置:決定 request 的 size class → 對該 list first-fit 搜尋 → 找到就(選擇性)split,碎片插回適當 list;找不到就搜尋下一個更大 class;全部都沒有才向 OS 要記憶體,配置後把剩餘放入適當 class。
- 釋放:coalesce 後放回適當的 free list。
- 是 GNU malloc(C 標準函式庫)等 production-quality 配置器的選擇:又快又省記憶體——搜尋侷限在 heap 的特定部分;且 segregated list 上的簡單 first-fit 搜尋近似整個 heap 的 best-fit 搜尋,utilization 佳。
Buddy Systems (p.901)
segregated fits 的特例:每個 size class 都是 2 的冪。heap 為
allocate 2^k:
找最小的可用 2^j (k <= j <= m)
j == k ? 完成
: 反覆對半分割直到 j == k,
每次分出的另一半 (buddy) 放入對應 free list
free 2^k:
與 free 的 buddy 持續合併,遇到 allocated buddy 即停止
關鍵性質:由 block 的位址與大小可直接算出 buddy 的位址——兩者恰好差一個 bit。例:size 32 bytes、位址 xxx...x00000 的 block,其 buddy 在 xxx...x10000。
- 優點:搜尋與合併都快。
- 缺點:size 必須是 2 的冪 → 可能嚴重 internal fragmentation,不適合通用 workload;適合 block sizes 已知為 2 的冪的特定應用。
各種 Free List 組織比較
| 組織 | allocate 時間 | free 時間 | utilization | 備註 |
|---|---|---|---|---|
| Implicit free list | O(總 block 數) | 常數(含 boundary-tag coalescing) | 取決於 placement | 最簡單;教學用 |
| Explicit (LIFO) | O(free block 數) | 常數 | 較差 | 需 pred/succ → 最小 block 變大 |
| Explicit (address order) | O(free block 數) | 線性 | 接近 best fit | 找 predecessor 需搜尋 |
| Simple segregated storage | 常數 | 常數 | 差(不 split/coalesce) | 最小 block = 1 word |
| Segregated fits | 限定於部分 heap 的搜尋 | 常數級 | 佳(近似 best fit) | GNU malloc 採用 |
| Buddy system | 快 | 快(buddy 合併) | internal fragmentation 嚴重 | size 皆為 2 的冪 |
Exam/Test Patterns
| 情境 / 關鍵字 | 答案 |
|---|---|
| malloc 回傳位址的對齊 | 32-bit mode:8 的倍數;64-bit mode:16 的倍數 |
| 要「清零」的動態記憶體 / 改變 block 大小 | calloc / realloc |
sbrk(0) 回傳什麼 |
目前的 brk 值(heap 頂端) |
| 求 header 值(size 24、allocated) | 0x18 | 0x1 = 0x19(size 含 header/padding;低 3 bits 中最低位是 alloc bit) |
| 從 header 取 size / alloc bit | GET(p) & ~0x7 / GET(p) & 0x1 |
| 計算 peak utilization | |
| 「總 free 空間夠,單一 block 不夠」 | external fragmentation(取決於未來 requests,難量化) |
| 「block 比 payload 大」(對齊/最小 size) | internal fragmentation(= block size − payload 總和) |
| 為何 free 後相鄰小 free blocks 無法滿足請求 | false fragmentation → 需要 coalescing |
| 常數時間合併「前一個」block 的技術 | Knuth 的 boundary tags(footer = header 複本) |
| allocated block 何時可省 footer | 把前一 block 的 alloc bit 存入 current header 的多餘低位 bit;free block 仍需 footer |
| prologue/epilogue block 的目的 | 消除 coalescing 的邊界條件檢查(皆標記 allocated;epilogue size=0) |
| mm_malloc 的 asize 計算 | size<=8 → 16;否則 8*((size+8+7)/8)(加 header/footer overhead 再向上取整到 8) |
| first fit vs next fit vs best fit | first fit 留大塊在尾端但開頭生 splinters;next fit 較快但 utilization 較差;best fit utilization 最佳但需徹底搜尋 |
| implicit vs explicit list 的 allocate 複雜度 | 總 block 數線性 → free block 數線性 |
| LIFO vs address-ordered explicit list | LIFO free 常數時間;address order free 線性但 utilization 接近 best fit |
| simple segregated storage 最小 block size | 1 word(只需 free block 的 succ 指標;無 header/footer、不 split、不 coalesce) |
| GNU malloc 採用的組織 | segregated fits(快且省記憶體;first fit 近似全 heap best fit) |
| buddy 位址計算 | block 與 buddy 位址恰差一個 bit:size x...x00000 → buddy x...x10000 |
| free 一個非 malloc 回傳的指標 | undefined behavior,且 free 不回報錯誤 → 詭異的 run-time errors(見 9.11) |
常見誤區:(1) header 的 size 是整個 block(含 header、payload、padding),不是 payload;(2) free 之後指標仍指向原處(dangling pointer),不使用它是應用程式的責任;(3) allocator 不允許 compaction,所以 external fragmentation 無法靠搬動解決;(4) FTRP、NEXT_BLKP 等巨集依賴 HDRP(bp) 中 size 已正確——coalesce 中更新順序錯誤會算錯位址。