前段時間一直想看malloc的原理,在搜了好幾篇malloc源碼后遂放棄,晦澀難懂。
后來室友買了本深入理解計算機系統的書,原來上面有講malloc的原理,遂看了,先明白理論,在看代碼就理解比較快了= =
1.問題的引入:
為什么要使用malloc,主要是因為在代碼中,為了節約內存,很多數據都是動態生成的,所以會用malloc,對應於C++中的new,底層還是調用malloc.
2.碎片的問題:
會有內部碎片與外部碎片的問題,內部碎片難以消除(因為字對齊之類的問題),而外部碎片是可以消除的(如果不消除的話,外部的內存塊越來越小,雖然數量多了,但是利用率會急劇下降!)
3.需要解決的問題:
4.隱式的空閑鏈表:
一個簡單的堆塊,這里只有頭部,下面為了優化,還會使用尾部。
隱式的空閑鏈表:
這樣就把用鏈表的方法把堆空間給聯系起來了。特點明顯,實現簡單,但分配時查詢空堆塊是線性時間的。
5.放置以分配的塊:
1)、首次適配:從鏈表開始尋找適合的空堆塊,直到找到為止
2)、下一次適配:從上一次的分配點開始找,直到找到適合的空堆塊為止
3)、最佳適配:查詢整個鏈表,找到最合適(浪費最小)的空堆塊
6.分割空閑塊:
當前的分配的空堆塊比所需要的空間大很多的時候,如果不分割,就會照成內部碎片過大,利用率下降。
當需要3個字節的空間時,首次適配找到了滿足條件的第二個堆塊,但堆塊過大,所以分割成兩個16字節的堆塊。
7.獲取額外的堆存儲器:
當鏈表中不能滿足申請要求的堆塊空間的時候,1)通過合並相鄰的堆塊空間,形成單個盡量大的堆塊空間 2)實在沒有其他辦法了,分配器通過sbrk函數向內核申請格外的堆空間,分配器將堆空間插入到鏈表中,然后提供給申請空間的塊。
8.合並
如果,在如上的情況下,合並兩個塊,由於當前塊不知道上一個塊的情況,它要通過遍歷整個鏈表才能知道上一個堆塊的空間是否為空,這樣將花費線性的時間來合並與當前相鄰的空閑堆塊。有個類似雙向鏈表的方法(不過不是真正的雙向鏈表,只是可以加一個信息量,直接就可以知道上一個堆塊的情況)
上一個堆塊的情況就容易知道了,下面有四種情況:
這樣,堆塊合並的問題就解決了。
參考:深入理解計算機系統