自訂記憶體配置器

切割與合併區塊

想像一個長架子,你把盒子一個挨一個地放上去。兩個日常動作讓它保持好用。如果有人要一個小盒子,但唯一的空位很大,你就切割那個空位:交出他需要的小塊,剩下的留成一個較小的空位。如果兩個空位最後剛好緊鄰,你就合併它們:拆掉中間那道牆,當成一個更大的空位,好讓未來的大盒子放得進去。切割(splitting)與合併(coalescing)對記憶體區塊就是這兩個動作,兩者合力使堆積不致腐化成一堆無用的小碎片。

切割發生在配置時。假設最小的合適空閒區塊是 128 位元組,但請求只要 32。配置器不會把整個 128 都交出去(那會在區塊內浪費 96 個位元組——內部碎片),而是把它切割:割出一塊 32 位元組的區塊回傳,替剩下的 96 位元組區塊寫一個新標頭,再把這個剩餘塊放回自由串列。合併發生在釋放時。當你釋放一個區塊,配置器檢查它緊鄰的鄰居(用邊界標籤往左看,按大小往前跳往右看):對每個同樣空閒的鄰居,就把它們的大小相加、重寫一個標頭,併成一個更大的空閒區塊。於是釋放三個相鄰的 64 位元組區塊,最後會變成單一個 192 位元組的空閒區塊,可供較大的請求使用。

為何重要:若不合併,一個配置許多小區塊又把它們釋放的程式,會讓堆積塞滿小空洞——總空閒記憶體很多,卻沒有任何一塊大到足以滿足大型請求(這就是外部碎片)。合併是對抗它的主要防線。誠實的張力:每次釋放都合併會花時間,而積極切割會製造更多小區塊。因此許多配置器會延後合併(批次處理),或在小物件的快速路徑上跳過它、只合併較大的區域,用一點碎片換取速度。

/* 切割一塊 128 位元組的空閒區塊以服務 32 位元組請求 */ /* 之前: [ 128 空閒 ] */ /* 之後: [ 32 使用中 ][ 96 空閒 ] <- 剩餘塊重新入串列 */ /* 釋放時合併:三個相鄰空閒區塊變成一個 */ /* [64 空閒][64 空閒][64 空閒] -> [ 192 空閒 ] */

切割以免用大區塊浪費在小請求上;合併以從小空閒塊重建大空閒塊。

合併只能併接實體上相鄰的空閒區塊;在定址空間中相距甚遠的兩個空閒區塊無法接合,因為配置器不可把夾在中間的使用中區塊搬開。

又稱
splittingcoalescingmerging free blocks切割合併區塊合併