自訂記憶體配置器

內部碎片與外部碎片

碎片是你擁有卻無法使用的浪費記憶體,它有兩種容易混淆的不同風味。想像用紙箱搬家。內部碎片(internal fragmentation)是你把一個小物件放進一個大箱子,箱子裡大半是空氣——空間被佔用了(箱子被拿走了)但內部被浪費。外部碎片(external fragmentation)是你的箱子都裝滿了,卻散落在房間各處、彼此之間留著尷尬的縫隙,於是雖然總空地很多,卻沒有任何單一縫隙大到放得下衣櫃。總浪費相同,問題的形狀卻完全不同。

在配置器中精確地說:內部碎片是已配置區塊「內部」的未用空間——程式所要求的,與它實際拿到的較大區塊之間的落差。它來自向上湊到 size class 或對齊(要 50 位元組卻拿到 64 位元組的區塊,浪費 14),或來自配置器存在你資料旁邊的標頭。浪費住在「使用中」的區塊內。外部碎片是已配置區塊「外部」的未用空間——總空閒記憶體很充裕,卻被切成許多不連續的小空閒區塊,於是一個大型請求會失敗,即使空閒位元組的總和明明足夠。它隨時間累積,因為不同大小的物件混雜地被配置與釋放,留下一塊像瑞士起司般滿是孔洞的記憶體。

每一個放置與結構選擇都在對抗其中一個、卻犧牲另一個。best-fit 與緊密的 size class 減少內部浪費,卻可能倍增微小的外部孔洞;粗糙的 size class 與夥伴系統的 2 的次方湊整接受內部浪費,以換取簡單的合併並把外部碎片壓低。合併相鄰的空閒區塊是對抗外部碎片的主要武器,但若一個長壽區塊坐在中間把一切釘住,它就無能為力。誠實的定位:一個無法搬動使用中物件的通用配置器永遠無法把兩者都壓到零——它只能選擇把浪費花在哪裡。(會搬移/壓實的垃圾回收器之所以能消除外部碎片,正因為它被允許搬遷物件,而 malloc 不行。)

/* 內部:要 50,拿到 64 位元組類別區塊 -> 14 位元組浪費在「內部」 */ /* [ 50 使用中 | 14 未用 ] */ /* 外部:總共 100 位元組空閒,卻是散落的: */ /* [16 空閒][使用中][16 空閒][使用中][16 空閒] ... */ /* malloc(48) 失敗——沒有任何單一空閒區塊夠大 */

內部浪費在使用中的區塊裡;外部浪費是大量空閒記憶體被切成小到無法使用的碎塊。

空閒位元組比你的請求多,並不保證配置會成功:外部碎片可能讓 malloc() 失敗,即使總空閒空間超過請求量,因為沒有任何單一連續區塊放得下。

又稱
fragmentationmemory fragmentation記憶體碎片內部碎裂外部碎裂