malloc/free函式的簡單實現及思考
用於記憶體管理的malloc/free這對函式,對於使用c語言的程式設計師應該很熟悉。前段時間聽說有的it公司以「實現乙個簡單功能的malloc」作為面試題,正好最近在複習k&r,上面有所介紹,因此花了些時間仔細研究了一下。畢竟把題目做出來是次要的,了解實現思想、提公升技術才是主要的。本文主要是對malloc/free實現思路的介紹,藍色部分文字是在個人思考中覺得比較核心的東西;另外對於**的說明,有一些k&r上的解釋,使用綠色加亮。
在研究k&r第八章第七節的實現之前,不妨先看看其第五章第四節的alloc/afree實現,雖然這段**主要目的是展示位址運算。
alloc實現
這種簡單實現的缺點:
1.作為代表記憶體資源的allocbuf,其實是預先分配好的,可能存在浪費。
2.分配和釋放的順序類似於棧,即「後進先出」,釋放時如果不按順序會造成異常。
這個實現雖然比較簡陋,但是依然提供了乙個思路。如果能把這兩個缺點消除,就能夠實現比較理想的malloc/free。
僅僅依靠位址運算來進行定位,是限制分配**靈活性的原因,它要求已使用部分和未使用部分必須通過某個位址分開成兩個相鄰區域。為了能讓這兩個區域能夠互相交錯,甚至其中還包括一些沒有分配的位址空間,需要使用指標把同類的記憶體空間連線起來形成鍊錶,這樣就可以處理位址不連續的一系列記憶體空間。但是為什麼只連線了空閒空間而不連線使用中的空間?這麼問可能出於在對圖中二者模擬時的直覺而沒有經過思考,這很簡單,因為沒有必要。前者相互鏈結是為了能夠在記憶體分配時遍歷所有空閒空間,並且在使用free()**已使用空間時進行重新插入。而對於使用中的空間,由於我們在分配空間時已經知道它們的位址了,**時可以直接告訴free(),並不用像malloc()時進行遍歷。
既然提到了鍊錶,可能對資料結構稍有了解的人會立刻寫下乙個struct來代表乙個記憶體區域,其中包含乙個指向下乙個記憶體區域的指標,但是這個struct的其他成員該怎麼寫呢?作為待分配的記憶體區域,大小是不定的,如果把它宣告為struct的成員變數顯然不妥;如果宣告為乙個指向某個其他的區域的指標,這似乎又和上面的直觀表示不相符合。(當然,這麼做也是可以實現的,它看上去是介於上圖的兩者之間,把管理結構和實際分配的空間相剝離,在文末我會專門的討論一下這種實現方法)因此,這裡仍然把控制結構和空閒空間相分開,但保持它們在記憶體位址中相鄰,形成下圖的形式,而正由這個特點,我們可以利用對控制結構指標的指標運算來定位對應的記憶體區域:
對應地,把控制資訊定義為header:
複製**
typedef long align;/for alignment to long boundary/
union header s;
align x;
};typedef union header header;
複製**
使用union而不是直接使用struct的原因是為了位址對齊。這裡是long對齊,union的x永遠不會使用。
這樣,malloc的主要工作就是對這些header和其後的記憶體塊的管理。
malloc()
實際分配的空間是header大小的整數倍,並且多出乙個header大小的空間用於放置header。但是直觀來看這並不是nunits = (nbytes+sizeof(header)-1)/sizeof(header) + 1啊?如果用(nbytes+sizeof(header))/sizeof(header)+1豈不是剛好?其實不是這樣,如果使用後者,(nbytes+sizeof(header))%sizeof(header) == 0時,又多分配了乙個header大小的空間了,因此還要在小括號裡減去1,這時才能符合要求。
malloc()第一次呼叫時建立乙個退化鍊錶base,只有乙個大小是0的空間,並指向它自己。freep用於標識空閒鍊錶的某個元素,每次查詢時可能發生變化;中間的查詢和分配過程是基本的鍊錶操作,在空閒鍊錶中不存在合適大小的空閒空間時呼叫morecore()獲得更多記憶體空間;最後的返回值是空閒空間的首位址,即header之後的位址,這個介面與庫函式一致。
morecore()
morecore()從系統申請更多的可用空間,並加入。由於呼叫了sbrk(),系統開銷比較大,為避免morecore()本身的呼叫次數,設定了乙個nalloc,如果每次申請的空間小於nalloc,就申請nalloc大小的空間,使得後續malloc()不必每次都需要呼叫morecore()。對於sbrk(),在後面會有介紹。
這裡有個讓人驚訝的地方:malloc()呼叫了morecore(),morecore()又呼叫了free()!第一次看到這裡時可能會覺得不可思議,因為按照慣性思維,malloc()和free()似乎應該是相互分開的,各司其職啊?但請再思考一下,free()是把空閒鍊錶進行擴充,而malloc()在空閒鍊錶不足時,從系統申請到更多記憶體空間後,也要先把它們轉化成空閒鍊錶的一部分,再進行利用。這樣,malloc()呼叫free()完成後面的工作也是順理成章了。根據這個思想,後面是free()的實現。在此之前,還有幾個morecore()自身的細節:
1.如果系統也沒有空間可以分配,sbrk()返回-1。cp是char *型別,在有的機器上char無符號,這裡需要一次強制型別轉換。
2.morecore()呼叫的返回值看上去比較奇怪,別擔心,freep會在free()中修改的。使用這個返回值也是為了在malloc()裡的判斷、p = freep的再次賦值的語句能夠緊湊。
free()
free()首先定位要釋放的ap對應的bp與空閒鍊錶的相對位置,找到它的的最近的上乙個和下乙個空閒空間,或是當它在整個空閒空間的前面或後面時找到空閒鍊錶的首尾元素。注意,由於malloc()的分配方式和free()的**時的合併方式(下文馬上要提到),可以保證整個空閒空間的鍊錶總是從低位址逐個公升高,在最高位址的空閒空間回指向低位址第乙個空閒空間。
定位後,根據要釋放的空間與附近空間的相鄰性,進行合併,也即修改對應空間的header。兩個if並列可以使得bp可以同時與高位址和低位址空閒空間結合(如果都相鄰),或者進行二者之一的合併,或者不合併。
完成了這三部分**後(注意放到同一原始檔中,sbrk()需要#include
malloc free函式的簡單實現及思考
用於記憶體管理的malloc free這對函式,對於使用c語言的程式設計師應該很熟悉。前段時間聽說有的it公司以 實現乙個簡單功能的malloc 作為面試題,正好最近在複習k r,上面有所介紹,因此花了些時間仔細研究了一下。畢竟把題目做出來是次要的,了解實現思想 提公升技術才是主要的。本文主要是對m...
動態記憶體分配 malloc,free 函式分析
c語言中的一切操作都是基於記憶體的。變數和陣列都是記憶體的別名,如何分配這些記憶體由編 譯器在編譯期間決定。定義陣列的時候必須指定陣列長度。而陣列長度是在編譯期就必須決定的 但是實際中有需求 程式執行的過程中,可能需要使用一些額外的記憶體空間。因此就需要採用動態記憶體分配。malloc和free用於...
malloc free記憶體碎片的產生原因
malloc和free大量使用後回造成記憶體碎片,那麼這種碎片形成的機理是什麼?如果機理是申請的記憶體空間大小 太小 所形成的,那麼申請多大的區域能夠最大限度的避免記憶體碎片呢 這裡的避免不是絕對的避免,只是一種概率 記憶體碎片一般是由於空閒的連續空間比要申請的空間小,導致這些小記憶體塊不能被利用....