1. 程式人生 > >glibc 記憶體池管理 ptmalloc(轉)

glibc 記憶體池管理 ptmalloc(轉)

Linux中malloc的早期版本是由DougLea實現的,它有一個重要問題就是在並行處理時多個執行緒共享程序的記憶體空間,各執行緒可能併發請求記憶體,在這種情況下應該如何保證分配和回收的正確和有效。Wolfram Gloger在Doug Lea的基礎上改進使得glibc的malloc可以支援多執行緒——ptmalloc,在glibc-2.3.x.中已經集成了ptmalloc2,這就是我們平時使用的malloc,目前ptmalloc的最新版本ptmalloc3。

其做法是,為了支援多執行緒並行處理時對於記憶體的併發請求操作,malloc的實現中把全域性使用者堆(heap)劃分成很多子堆(sub-heap)。這些子堆是按照迴圈單鏈表的形式組織起來的。每一個子堆利用互斥鎖(mutex)使執行緒對於該子堆的訪問互斥。當某一執行緒需要呼叫malloc

分配記憶體空間時,該執行緒搜尋迴圈連結串列試圖獲得一個沒有加鎖的子堆。如果所有的子堆都已經加鎖,那麼malloc會開闢一塊新的子堆,對於新開闢的子堆預設情況下是不加鎖的,因此執行緒不需要阻塞就可以獲得一個新的子堆並進行分配操作。在回收free操作中,執行緒同樣試圖獲得待回收塊所在子堆的鎖,如果該子堆正在被別的執行緒使用,則需要等待直到其他執行緒釋放該子堆的互斥鎖之後才可以進行回收操作。

         申請小塊記憶體時會產生很多記憶體碎片,ptmalloc在整理時需要對子堆做加鎖操作,每個加鎖操作大概需要5~10個cpu指令,而且程式執行緒數很高的情況下,鎖等待的時間就會延長,導致malloc效能下降。

因此很多大型的服務端應用會自己自己實現記憶體池,以降低向系統malloc的開銷。HoardTCmalloc是在glibc和應用程式之間實現的記憶體管理。Hoard的作者是美國麻省的Amherst College的一名老師,理論角度對hoard的研究和優化比較多,相關的文獻可以hoard主頁下載到到。從我自己專案中的系統使用來看,Hoard確實能夠很大程度的提高程式的效能和穩定性。TCMalloc(Thread-Caching Malloc)是google開發的開源工具──“google-perftools”中的成員。這裡有它的系統的介紹安裝方法。因為人品原因,我第一次編譯tcmalloc沒有通過,有因為決定為自己的程式實現記憶體管理,這個就淺嘗輒止了。


ptmalloc

phenix* 

2006-06-07

目錄

    1  前言

    2  x86平臺Linux程式的記憶體分佈

    3  Allocator

    4  chuck的組織

        4.1  chuck

        4.2  chunk中的空間複用

    5  空閒 chunk 容器

        5.1  Bins

        5.2  Fastbins

        5.3  Unsorted Bins

        5.4  例外的 chunk

    6  sbrk & mmap 

        6.1  sbrk

        6.2  mmap

    7  malloc()

    8  free()

1  前言

C語言提供了動態記憶體管理功能, 在C語言中, 程式設計師可以使用 malloc() 和 free() 函式顯式的分配和釋放記憶體. 關於 malloc() 和free() 函式, C語言標準只是規定了它們需要實現的功能, 而沒有對實現方式有什麼限制, 這多少讓那些追根究底的人感到有些許迷茫, 比如對於 free() 函式, 它規定一旦一個記憶體區域被釋放掉, 那麼就不應該再對其進行任何引用, 任何對釋放區域的引用都會導致不可預知的後果 (unperdictable effects). 那麼, 到底是什麼樣的不可預知後果呢? 這完全取決於記憶體分配器(memory allocator)使用的演算法. 這篇文章試圖對 Linux glibc 提供的 allocator 的工作方式進行一些描述, 並希望可以解答上述類似的問題. 雖然這裡的描述侷限於特定的平臺, 但一般的事實是, 相同功能的軟體基本上都會採用相似的技術. 這裡所描述的原理也許在別的環境下會仍然有效. 另外還要強調的一點是, 本文只是側重於一般原理的描述, 而不會過分糾纏於細節, 如果需要特定的細節知識, 請參考特定 allocator 的原始碼. 最後, 本文描述的硬體平臺是 Intel 80x86, 其中涉及的有些原理和資料可能是平臺相關的. 

因為只是草草看了 ptmalloc 的原始碼, 並做了一些實驗, 而沒有仔細分析程式碼. 所以文章中的一些內容難免不實, 甚至為虛妄. 實在是因為水平有限, 並非存心妄自揣測, 來愚人耳目. 如果讀者發現其中有任何錯誤, 請來信告之, 並歡迎來信討論. 另外, 文章中涉及一些闕值, 比如記憶體分配的位置, 以及 max_fast 大小等等, 會因具體的實現而異, 若與所述有出入, 請自己判斷原因.

2  x86平臺Linux程式的記憶體分佈

Linux 程式載入記憶體後, loader 會把可執行檔案中的各個段依次載入到從某一地址開始的空間中(載入地址取決於 link editor(ld), 在我的機器上是0x8048000, 即128M處). 如圖1所 示, 首先被載入的是 “.text” 段, 然後是 “.data” 段, 最後是 “.bss” 段. 這可以看作是程式的開始空間. 程式所能訪問的最後的地址是0xbfffffff, 也就是到3G地址處, 3G以上的1G空間是核心使用的, 應用程式不可以直接訪問. 應用程式的堆疊從最高地址處開始向下生長, “.bss”段與堆疊之間的空間是空閒的. 這個區域可以供使用者自由使用, 但是它在剛開始的時候並沒有對映到記憶體空間內, 是不可訪問的. 在向核心請求分配該空間之前, 對這個空間的訪問會導致一個 “segmentation fault”. 使用者程式可以直接使用系統呼叫來管理這塊空間, 但更多的時候都是程式都是使用C語言提供的 malloc() 和 free() 函式來動態的申請和釋放記憶體. 

 

圖 1:  Linux程式記憶體分佈示意圖 

3  Allocator

GNU Libc 的記憶體分配器( allocator ) — ptmalloc 起源於 Doug Lea 的 malloc (請參看[1]). ptmalloc 實現了 malloc() , free() 以及一組其它的函式. 以提供動態記憶體管理的支援. allocator 處在使用者程式和核心之間, 它響應使用者的分配請求, 向作業系統申請記憶體, 然後將其返回給使用者程式, 為了保持高效的分配, allocator 一般都會預先分配一塊大於使用者請求的記憶體, 並通過某種演算法管理這塊記憶體. 來滿足使用者的記憶體分配要求, 使用者 free 掉的記憶體也並不是立即就返回給作業系統, 相反, allocator 會管理這些被 free 掉的空閒空間, 以應對使用者以後的記憶體分配要求. 也就是說, allocator 不但要管理已分配的記憶體塊, 還需要管理空閒的記憶體塊, 當響應使用者分配要求時, allocator 會首先在空閒空間中尋找一塊合適的記憶體給使用者, 在空閒空間中找不到的情況下才分配一塊新的記憶體. 為實現一個高效的 allocator, 需要考慮很多的因素. 比如, allocator 本身管理記憶體塊所佔用的記憶體空間必須很小, 分配演算法必須要足夠的快. Jonathan Bartlett 給出了一個簡單的 allocator 實現[2], 事先看看或許會對理解本文有所幫助. 另外插一句, Jonathan Bartlett 的書 “Programming from Ground Up” 對想要了解 linux 彙編和工作方式的入門者是個不錯的選擇.

4  chuck的組織

不管記憶體是在哪裡被分配的, 用什麼方法分配, 使用者請求分配的空間在 ptmalloc 中都使用一個 chunk 來表示. 使用者呼叫 free() 函式釋放掉的記憶體也並不是立即就歸還給作業系統, 相反, 它們也會被表示為一個 chunk, ptmalloc 使用特定的資料結構來管理這些空閒的 chuck. 

4.1  chuck

ptmalloc 在給使用者分配的空間的前後加上了一些控制資訊, 用這樣的方法來記錄分配的資訊, 以便完成分配和釋放工作. 一個使用中的chuck( 使用中, 就是指還沒有被free掉 ) 在記憶體中的樣子如圖2所示. 

圖 2:   使用中的chuck 

在圖中, chunk 指標指向一個 chunk 的開始, 一個chunk 中包含了使用者請求的記憶體區域和相關的控制資訊. 圖中的 mem 指標才是真正返回給使用者的記憶體指標. chunk 的第二個域的最低一位為p, 它表示前一個塊是否在使用中, p為0則表示前一個 chunk 為空閒, 這時 chunk 的第一個域 prev_size 才有效, prev_size 表示前一個 chunk 的 size, 程式可以使用這個值來找到前一個 chunk 的開始. 當p為1時, 表示前一個 chunk 正在使用中, prev_size 無效, 程式也就不可以得到前一個 chunk 的大小. 而不能對前一個 chunk 進行任何操作. ptmalloc 分配的第一個塊總是將p設為1, 以防止程式引用到不存在的區域.

空閒 chunk 在記憶體中的結構如圖3所示, 

圖 3:  空閒的thunk 

當 chunk 空閒時, 原本是使用者資料區的地方儲存了兩個指標, 指標 fd 指向後一個空閒的 chunk, 而 bk 指向前一個空閒的 chunk, ptmalloc 通過這兩個指標將大小相近的 chunk 連成一個雙向連結串列. 而不同的 chunk 連結串列又是通過 bins 或者 fastbins 來組織的(bins 在第5.1節介紹, fastbins 在第5.2節介紹).

4.2  chunk中的空間複用

為了使得 chunk 所佔用的空間最小, ptmalloc 使用了空間複用, 一個 chunk 或者正在被使用, 或者已經被 free 掉, 所以 chunk 的中的一些域可以在使用狀態和空閒狀態表示不同的意義, 來達到空間複用的效果. 空閒時, 一個 chunk 中至少要4個 size_t 大小的空間, 用來儲存 prev_size, size , fd 和 bk (見圖3所 示). 也就是16 bytes. chuck 的大小要 align 到8 bytes. 當一個 chunk 處於使用狀態時, 它的下一個 chunk 的 prev_size 域肯定是無效的. 所以實際上, 這個空間也可以被當前 chunk 使用. 這聽起來有點不可思議, 但確實是合理空間複用的例子. 故而實際上, 一個使用中的 chunk 的大小的計算公式應該是: 

[xleftmargin=1cm] in_use_size = ( 使用者請求大小 + 8 - 4 ) align to 8 bytes 這裡加8是因為需要儲存 prev_size 和 size, 但又因為向下一個 chunk “借”了4個bytes, 所以要減去4. 最後, 因為空閒的 chunk 和使用中的 chunk 使用的是同一塊空間. 所以肯定要取其中最大者作為實際的分配空間. 即最終的分配空間 chunk_size = max(in_use_size, 16). 這就是當用戶請求記憶體分配時, ptmalloc 實際需要分配的記憶體大小, 在後面的介紹中. 如果不是特別指明的地方, 指的都是這個經過轉換的實際需要分配的記憶體大小, 而不是使用者請求的記憶體分配大小. 

5  空閒 chunk 容器

5.1  Bins

使用者 free 掉的記憶體並不是都會馬上歸還給系統, 相反, ptmalloc 會統一管理 heap 中的空閒的 chunk (關於heap, 請參照第6節中圖5), 當用戶進行下一次分配請求時, ptmalloc 會首先試圖在 heap 中空閒的 chunk 中挑選一塊給使用者, 這樣就避免了頻繁的系統呼叫, 降低了記憶體分配的開銷. ptmalloc 將 heap 中相似大小的 chunk 用雙向連結串列連結起來, 這樣的一個連結串列被稱為一個bin. ptmalloc 共維護了128個bin, 並使用一個數組來儲存這些 bin(如圖4). 

 

圖 4:  bins 結構示意圖 

陣列中的前64個 bin 稱為 “exact bins”, “exact bins” 中的 chunk 具有相同的大小. 兩個相鄰的 bin 中的 chunk 大小相差8 bytes. “exact bins”中的 chunk 按照最近使用順序進行排列, 最後釋放的 chunk 被連結到連結串列的頭部, 而 allocation 是從尾部開始, 這樣, 每一個 chunk 都有相同的機會被 ptmalloc 選中. 後面的 bin 被稱作 “ordered bins”. “ordered bins” 中的每一個 bin 分別包含了一個給定範圍內的 chunk, 其中的 chunk 按大小序排列. 相同大小的 chunk 同樣按照最近使用順序排列. ptmalloc 使用 “smallest-first, best-fit” 原則在空閒 “ordered bins” 中查詢合適的 chunk.

當空閒的 chunk 被連結到bin中的時候, ptmalloc 會把表示該 chunk 是否處於使用中的標誌 p 設為0(注意, 這個標誌實際上處在下一個 chunk 中), 同時 ptmalloc 還會檢查它前後的 chunk 是否也是空閒的, 如果是的話, ptmalloc 會首先把它們合併為一個大的 chunk, 然後將合併後的 chunk 放到 bin 中. 要注意的是, 並不是所有的 chunk 被釋放後就立即被放到bin中. ptmalloc 為了提高分配的速度, 會把一些小的的 chunk 先放到一個叫做 fastbin的容器內.

5.2  Fastbins

一般的情況是, 程式在執行時會經常需要分配和釋放一些較小的記憶體空間. 當 allocator 合併了相鄰的幾個小的 chunk 之後, 也許馬上就會有另一個小塊記憶體的請求, 這樣 allocator 又需要從大的空閒記憶體中分出一塊出來, 這樣無疑是比較低效的, 故而, ptmalloc 中在分配過程中引入了 fastbins, 不大於 max_fast (72 bytes) 的 chunk 被 free 後, 首先會被放到 fastbins 中, fastbins 中的 chunk 並不改變它的使用標誌p. 這樣也就無法將它們合併, 當需要給使用者分配的 chunk 小於或等於 max_fast 時, ptmalloc 首先會在 fastbins 中查詢相應的空閒塊(具體的分配演算法請參考第7節), 然後才會去查詢 bins 中的空間 chunk. 在某個特定的時候, ptmalloc 會遍歷 fastbins 中的 chunk, 將相鄰的空閒 chunk 進行合併, 並將合併後的 chunk 放到 bins 中去.

5.3  Unsorted Bins

如果被使用者釋放的 chunk 大於 max_fast, 則按上面的敘述它應該會被放到 bins中. 但實際上, ptmalloc 還引入了一個稱為 “unsorted bins”的佇列. 這些大於 max_fast 的chunk 首先會被放到 “unsorted bins” 佇列中, 在進行 malloc 操作的時候, 如果在 fastbins 中沒有找到合適的 chunk, 則 ptmalloc 會先在 “unsorted bins”中查詢合適的空閒 chunk, 然後才查詢 bins. 如果 “unsorted bins” 不能滿足分配要求. malloc 便會將 “unsorted bins” 中的 chunk 放到 bins 中, 然後再在 bins 中繼續進行查詢和分配過程. 從這個過程可以看出來, “unsorted bins”可以看做是 bins 的一個緩衝區, 增加它只是為了加快分配的速度, 忽略它對我們理解 ptmalloc 沒有太大的影響, 在本文中, 這個過程就不被考慮了.

5.4  例外的 chunk

並不是所有的 chunk 都按照上面的方式來組織, 實際上, 有兩種例外情況. 

top chunk 

在前面一直提到, ptmalloc 會預先分配一塊較大的空閒記憶體(也就是所為的 heap), 而通過管理這塊記憶體來響應使用者的需求, 因為記憶體是按地址從低向高進行分配的, 在空閒記憶體的最高處, 必然存在著一塊空閒 chunk, 叫做 “top chunk”. 當 bins 和 fastbins 都不能滿足分配需要的時候, ptmalloc 會設法在 “top chunk” 中分出一塊記憶體給使用者, 如果 “top chunk” 本身不夠大, 則 ptmalloc 會適當的增加它的大小(也就增加了 heap 的大小). 以滿足分配的需要, 實際上, “top chunk” 在分配時總是在 ‘fastbins 和 bins 之後被考慮, 所以, 不論 “top chunk” 有多大, 它都不會被放到 fastbins 或者是 bins 中. 

mmaped chunk 

當需要分配的 chunk 足夠大, 而且 fastbins 和 bins 都不能滿足要求, 甚至 “top chunk” 本身也不能滿足分配需求時, ptmalloc 會使用 mmap 來直接使用記憶體對映來將頁對映到程序空間(具體的情況, 請參考第6節). 這樣分配的 chunk 在被 free 時將直接解除對映, 於是就將記憶體歸還給了系統, 再次對這樣的記憶體區的引用將導致一個 “segmentation fault” 錯誤. 這樣的 chunk 也不會包含在任何 bin 中. 

6  sbrk & mmap 

ptmalloc 使用兩種方法向記憶體索取記憶體空間: sbrk 和 mmap. 它們用於不同的場合. 

6.1  sbrk

如圖5所示, 

圖 5:  使用 sbrk 和 mmap 分配記憶體示意圖 

.bss 段之上的這塊分配給使用者程式的空間被稱為 heap (堆). start_brk 指向 heap 的開始, 而 brk 指向 heap 的頂部. 可以使用系統呼叫 brk 和 sbrk 來增加標識 heap 頂部的 brk 值, 從而線性的增加分配給使用者的 heap 空間. 在使用malloc之前, brk 的值等於start_brk, 也就是說 heap 大小為0. ptmalloc 在開始時, 若請求的空間小於 DEFAULT_MMAP_THRESHOLD (128K bytes)時, ptmalloc 會呼叫sbrk增加一塊大小為 ( 128 KB + chunk_size ) align 4K 的空間作為heap. 這就是前面所說的 ptmalloc 所維護的分配空間, 當用戶請求記憶體分配時, 首先會在這個區域內找一塊合適的 chunk 給使用者. 當用戶釋放了 heap 中的 chunk 時, ptmalloc 又會使用 fastbins 和 bins 來組織空閒 chunk. 以備使用者的下一次分配(具體的分配過程見第7節). 若需要分配的 chunk 大小小於 DEFAULT_MMAP_THRESHOLD, 而 heap 空間又不夠, 則此時 ptmalloc 會通過 sbrk 呼叫來增加 heap 值, 也就是增加 “top chunk”的大小, 每次 heap 增加的值都會 align 到4k bytes. 

6.2  mmap

當用戶的請求超過 DEFAULT_MMAP_THRESHOLD , 並且使用 sbrk 分配失敗的時候, ptmalloc 會嘗試使用 mmap 直接對映一塊記憶體到程序記憶體空間(我機器上是在0x40159000地址處). 使用 mmap 直接對映的 chunk 在釋放時直接解除對映, 而不再屬於程序的記憶體空間. 任何對該記憶體的訪問都會產生段錯誤. 而在 heap 中分配的空間則可能會留在程序記憶體空間內, 還可以再次引用(當然是很危險的).

7  malloc()

ptmalloc 的響應使用者記憶體分配要求的具體步驟為: 

獲取分配區的鎖, ptmalloc 對 Doug Lea malloc 的主要擴充套件便是增加了執行緒支援. 為了防止多個執行緒同時訪問同一個分配區, 在進行分配之前需要取得分配區域的鎖, 如果主分配區域的鎖不能得到, 那麼會 ptmalloc 會建立一個新的分配區域供當前執行緒使用. 

將使用者的請求大小轉換為實際需要分配的空間大小(見第4.2節的相關介紹). 

判斷所需分配 chunk 的大小是否滿足 chunk_size <= max_fast (max_fast 預設為 72 bytes) , 如果是的話, 則轉下一步, 否則跳到第5步. 

首先嚐試在 fastbins 中摘取一個所需大小的 chunk 分配給使用者. 如果可以找到, 則分配結束. 否則轉到下一步. 

判斷所需大小是否處在 “exact bins” 中, 即判斷 chunk_size  512 bytes 是否成立(見圖4). 如果 chunk 大小處在 “exact bins”中, 則轉下一步, 否則轉到第6步. 

根據所需分配的 chunk 的大小, 找到具體所在的 “exact bins”, 並從該 bin 的尾部摘取一塊恰好滿足大小的 chunk. 若成功, 則分配結束, 否則, 轉到下一步. 

到了這一步, 說明需要分配的是一塊大的記憶體, 或者, “exact bins” 中找不到合適的 chunk. 於是, ptmalloc 首先會遍歷 fastbins 中的 chunk , 將相鄰的 chunk 進行合併, 並連結到 bins 中, 然後從 “sorted bins” 中按照 “smallest-first, best-fit” 原則, 找一塊合適的 chunk, 從中劃分一塊所需大小的chunk, 並將剩下的部分連結回到 bins 中. 若操作成功, 則分配結束, 否則轉到下一步. 

如果搜尋 fastbins 和 bins 都沒有找到合適的 chunk, 那麼就需要操作 top chunk 來進行分配了. 判斷 top chunk 大小是否滿足所需 chunk 的大小, 如果是, 則從 top chunk 中分出一塊來. 否則轉到下一步. 

到了這一步, 說明 top chunk 也不能滿足分配要求, 所以, 於是就有了兩個選擇: 呼叫 sbrk, 增加 top chunk 大小; 或者使用 mmap 來直接分配. 在這裡, 需要依靠 chunk 的大小來決定到底使用哪種方法. 判斷所需分配的 chunk 大小是否大於等於 DEFAULT_MMAP_THRESHOLD (128KB), 如果是的話, 則轉下一步, 呼叫 mmap 分配, 否則跳到第11步, 使用 sbrk 來增加 top chunk 的大小. 

使用 mmap 系統呼叫在大約 0x40159000 (大約為1G) 地址處為程式的記憶體空間對映一塊 chunk_size align 4kB 大小的空間. 然後將記憶體指標返回給使用者. 

判斷是否為第一次呼叫 malloc, 若是, 則需要進行一次初始化工作, 分配一塊大小為 (chunk_size + 128K) align 4KB 大小的空間作為初始的 heap. 若已經初始化過了, 則呼叫 sbrk 增加 heap 空間, 使之滿足分配需求, 並將記憶體指標返回給使用者. 

總結一下: 根據使用者請求分配的記憶體的大小, ptmalloc 有可能會在兩個地方為使用者分配記憶體空間. 在第一次分配記憶體時, brk 值等於 start_brk, 所以實際上 heap 大小為0, top chunk 大小也是0. 這時, 如果不增加 heap 大小, 就不能滿足任何分配要求. 所以, 若使用者的請求小於 DEFAULT_MMAP_THRESHOLD, 則 ptmalloc 會初始化heap. 然後在 heap 中分配空間給使用者, 以後的分配就基於這個 heap 進行. 若第一次使用者的請求就大於DEFAULT_MMAP_THRESHOLD, 則 ptmalloc 直接使用 mmap 分配一塊給使用者, 而 heap 也就沒有被初始化, 直到使用者第一次請求小於 DEFAULT_MMAP_THRESHOLD 的記憶體分配. 第一次以後的分配就比較複雜了, 簡單說來, ptmalloc 首先會查詢 fastbins, 如果不能找到匹配的 chunk, 則查詢 “exact bins”. 若還是不行, 則查詢 “sorted bins”. 在 fastbins 和 “exact bins” 中的查詢都需要精確匹配, 而在sorted bins 中查詢時, 則遵循 “smallest-first, best-fit” 的原則, 不需要精確匹配. 若以上方法都失敗了, 則 ptmalloc 會考慮使用 top chunk. 若top chunk 也不能滿足分配要求. 而且所需 chunk 大小大於 DEFAULT_MMAP_THRESHOLD , 則使用 mmap 進行分配. 否則增加 heap. 增大 top chunk. 以滿足分配要求.

8  free()

free() 函式接受一個指向分配區域的指標作為引數, 釋放該指標所指向的 chunk. 而具體的釋放方法則看該 chunk 所處的位置和該 chunk 的大小. free()函式的工作步驟如下: 

free() 函式同樣首先需要獲取分配區的鎖, 來保證執行緒安全. 

判斷傳入的指標是否為0, 如果為0, 則什麼都不做, 直接return. 否則轉下一步: 

判斷所需釋放的 chunk 是否為 mmaped chunk, 如果是, 則直接釋放 mmaped chunk, 解除記憶體空間對映. 該空間不再有效. 釋放完成. 否則跳到下一步. 

判斷 chunk 的大小和所處的位置, 若 chunk_size <= max_fast , 並且 chunk 並不位於 heap 的頂部, 也就是說並不與 top chunk 相鄰, 則轉到下一步, 否則跳到第6步. (因為與 top chunk 相鄰的小 chunk 也和 top chunk 進行合併, 所以這裡不僅需要判斷大小, 還需要判斷相鄰情況.) 

將 chunk 放到 fastbins 中, chunk 放入到 fastbins 中時, 並不設定該 chunk 使用位. 也不與相鄰的 chunk 進行合併. 只是放進去, 如此而已. 做實驗的結果還發現ptmalloc 放入 fastbins 中的 chunk 中的使用者資料去全置為 0. 但是在原始碼中找不到相關的程式碼. 這一步做完之後釋放便結束了, 程式從 free() 函式中返回.. 

判斷前一個 chunk 是否處在使用中, 如果前一個塊也是空閒塊, 則合併. 並轉下一步. 

判斷當前釋放 chunk 的下一個塊是否為 top chunk, 如果是, 則轉第9步, 否則轉下一步. 

判斷下一個 chunk 是否處在使用中, 如果下一個 chunk 也是空閒的. 則合併, 並將合併後的 chunk 放到 bins 中. 注意, 這裡在合併的過程中, 要更新 chunk 的大小, 以反映合併後的 chunk 的大小. 並轉到第10步. 

如果執行到這一步, 說明釋放了一個與 top chunk 相鄰的chunk. 則無論它有多大, 都將它與 top chunk 合併, 並更新 top chunk 的大小等資訊. 轉下一步. 

判斷合併後的 chunk 的大小是否大於 FASTBIN_CONSOLIDATION_THRESHOLD, 如果是的話, 則會觸發進行 fastbins 的合併操作, fastbins 中的 chunk 將被遍歷, 並於相鄰的空閒 chunk 進行合併, 合併後的 chunk 會被放到 bins 中. fastbins 將變為空, 操作完成之後轉下一步. 

判斷 top chunk 的大小是否大於 DEFAULT_TRIM_THERESHOLD. 如果是的話, 則會試圖歸還 top chunk 中的一部分給作業系統. 但是最先分配的128KB的空間是不會歸還. ptmalloc 會一直控制這部分記憶體. 用於響應使用者的分配請求. 做完這一步之後, 釋放結束, 從 free 函式退出. 

參考文獻

[1]   Doug Lea. A Memory Allocator. http://gee.cs.oswego.edu/dl/html/malloc.html.  

[2]   Jonathan Bartlett. 記憶體管理內幕—動態分配的選擇、折衷和實現. http://www-128.ibm.com/developerworks/cn/linux/l-memory/