free() 函式只傳入一個記憶體地址,為什麼能知道要釋放多大的記憶體?
free()函式,要求傳入的引數必須是malloc的返回值。在進行malloc函式申請記憶體時,作業系統實際會申請大於malloc要求的長度。
malloc分配的記憶體為一個個chunk,以下是一個典型的 malloc_chunk 結構定義(以 glibc 為例):
structmalloc_chunk {
size_t prev_size; /* 前一個記憶體塊的大小(如果合併的話) */
size_t size; /* 當前記憶體塊的大小,包括邊界標記 */
structmalloc_chunk *fd;/* 指向前一個空閒記憶體塊的指標(用於空閒記憶體列表) */
structmalloc_chunk *bk;/* 指向下一個空閒記憶體塊的指標(用於空閒記憶體列表) */
};
prev_size 欄位表示前一個記憶體塊的大小(如果當前記憶體塊與前面的記憶體塊合併在一起)。size 欄位表示當前記憶體塊的大小,包括邊界標記。fd 和 bk 欄位分別表示指向前後空閒記憶體塊的指標,用於維護空閒記憶體列表。
當釋放一個記憶體塊時,記憶體分配器可以檢查 prev_size 欄位以確定前一個記憶體塊是否為空閒。如果前一個記憶體塊為空閒,記憶體分配器可以將這兩個相鄰的空閒記憶體塊合併成一個更大的空閒記憶體塊。這樣可以使記憶體分配更加高效,減少記憶體碎片
圖解chunk記憶體塊:

malloc返回的指標不是指向了Header,而是指向了Payload開始處。
所以空間的大小記錄在引數指標指向地址的前面,free的時候透過這個記錄即可知道要釋放的記憶體有多大。

不同的記憶體分配器實現不一樣,glibc的ptmalloc採用的是這種隱藏頭風格,gemalloc採用其他的實現方式。
關於malloc還有幾個知識點:
malloc是系統呼叫嗎?
malloc是C語言庫函式不是系統函式呼叫。
malloc 申請記憶體的時候,會有兩種方式向作業系統申請堆記憶體。
方式一:透過 brk() 系統呼叫從堆分配記憶體
方式二:透過 mmap() 系統呼叫在檔案對映區域分配記憶體;


malloc分配的是物理記憶體嗎?
答案:不是物理記憶體,是虛擬記憶體。
現在的作業系統,記憶體管理通常是基於虛擬記憶體的,所以應用程式看到的記憶體地址(虛擬地址)與實際的物理記憶體地址(物理地址)是不同的。作業系統透過記憶體管理單元(MMU)來將虛擬地址轉換為物理地址。
當應用程式首次訪問這塊記憶體時,作業系統發現對應的物理記憶體尚未分配,它會從可用的物理記憶體中分配相應的空間,並更新頁表項以完成虛擬地址到物理地址的對映。
如果這塊記憶體從來沒有被訪問,那麼就不會分配實際的物理記憶體,節約了記憶體。
呼叫free後,記憶體會被作業系統立馬回收嗎?
答案:不會。
當程式設計師使用free函式釋放記憶體後,這塊記憶體並不會立即被歸還給作業系統。相反,這些被釋放的記憶體首先會被記憶體管理器(如ptmalloc)儲存起來,以便後續重用。這樣做的主要目的是減少與作業系統的記憶體互動次數,從而降低系統呼叫的開銷。
記憶體管理器會使用雙鏈表等方式來管理這些被釋放的記憶體塊,當程式再次申請記憶體時,記憶體管理器會首先嚐試從這些已釋放的記憶體塊中找到合適的塊返回給程式,而不是直接向作業系統請求新的記憶體。這種機制有助於減少記憶體碎片和提高記憶體使用效率。