malloc free函式的簡單實現及思考

2021-09-22 07:29:40 字數 3663 閱讀 8872

用於記憶體管理的malloc/free這對函式,對於使用c語言的程式設計師應該很熟悉。前段時間聽說有的it公司以「實現乙個簡單功能的malloc」作為面試題,正好最近在複習k&r,上面有所介紹,因此花了些時間仔細研究了一下。畢竟把題目做出來是次要的,了解實現思想、提公升技術才是主要的。本文主要是對malloc/free實現思路的介紹,藍色部分文字是在個人思考中覺得比較核心的東西;另外對於**的說明,有一些k&r上的解釋,使用綠色加亮。

在研究k&r第八章第七節的實現之前,不妨先看看其第五章第四節的alloc/afree實現,雖然這段**主要目的是展示位址運算。

#define allocsize 10000

static

char allocbuf[allocsize]; /*

storage for alloc

*/static

char *allocp = allocbuf; /*

next free position

*/char *alloc(int n)

else

return0;}

void afree(char *p)

s; align x;

};typedef union header header;

使用union而不是直接使用struct的原因是為了位址對齊。這裡是long對齊,union的x永遠不會使用。

這樣,malloc的主要工作就是對這些header和其後的記憶體塊的管理。

static header base;

static header *freep = null;

void *malloc(unsigned nbytes)

for(p = prevp->s.ptr; ;prevp = p, p= p->s.ptr)

freep = prevp;

return (void*)(p+1);

}if (p== freep) /*

*/if ((p = morecore(nunits)) == null)

return null; /*

none left

*/ }

}

實際分配的空間是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之後的位址,這個介面與庫函式一致。

#define nalloc 1024    /* minimum #units to request */

static header *morecore(unsigned nu)

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的再次賦值的語句能夠緊湊。

void free(void *ap)

else

bp->s.ptr = p->s.ptr;

if (p+p->s.size == bp) else

p->s.ptr = bp;

freep = p;

}

free()首先定位要釋放的ap對應的bp與空閒鍊錶的相對位置,找到它的的最近的上乙個和下乙個空閒空間,或是當它在整個空閒空間的前面或後面時找到空閒鍊錶的首尾元素。注意,由於malloc()的分配方式和free()的**時的合併方式(下文馬上要提到),可以保證整個空閒空間的鍊錶總是從低位址逐個公升高,在最高位址的空閒空間回指向低位址第乙個空閒空間。

定位後,根據要釋放的空間與附近空間的相鄰性,進行合併,也即修改對應空間的header。兩個if並列可以使得bp可以同時與高位址和低位址空閒空間結合(如果都相鄰),或者進行二者之一的合併,或者不合併。

完成了這三部分**後(注意放到同一原始檔中,sbrk()需要#include ),就可以使用了。當然要注意,命名和stdlib.h中的同名函式是衝突的,可以自行改名。

第一次審視原始碼,會發現很多實現可能原先並沒有想到:header的結構和對齊填充、空間的取整、鍊錶的操作和初始化(邊界情況)、malloc()對free()的呼叫、由malloc()和free()暗中保證的鍊錶位址有序等等,確實很值得玩味。另外再附上前文中提到的兩個問題還有一些補充問題的簡單思考

1.header與空閒空間相剝離,header中包含乙個指向其空閒空間的指標

這樣做未必不可,相應地演算法需要改動。同時,由於header和空閒空間不再相鄰,sbrk()獲得的空間也應該包含header的部分,記憶體的分布可能會更加瑣碎。當然,這也可能帶來好處,即用其他資料結構對鍊錶進行管理,比如按大小進行hash,這樣查詢起來更快。

2.關於sbrk()

sbrk()也是庫函式,它能使堆往棧的方向增長,具體可以參考:brk(), sbrk() 用法詳解。

3.可以改進的方

空閒空間的尋找是線性的,查詢過程在記憶體分配中可以看作是迴圈首次適應演算法,在某些情況下可能很慢;如果再建立乙個資料結構,如hash表,對不同大小的空間進行索引,肯定可以加快查詢本身,並且能實現一些演算法,比如最佳匹配。但查詢加快的代價是,修改這個索引會占用額外的時間,這是需要權衡的。

morecore()中的最小分配空間是巨集定義,在實際使用中完全可以作為引數傳遞,根據需要設定最小分配下限。

2013.7.27更新:

4.這個malloc()和系統提供的malloc()的差別?

其實庫函式的malloc在引數為0時可以返回乙個null,而以上寫malloc在頭部分配成功後總不會返回null。這是差別之一,換句話說,二者實現還是不同的,這裡只是為了演示malloc的原理,並非真正的庫函式。

malloc free函式的簡單實現及思考

malloc free函式的簡單實現及思考 用於記憶體管理的malloc free這對函式,對於使用c語言的程式設計師應該很熟悉。前段時間聽說有的it公司以 實現乙個簡單功能的malloc 作為面試題,正好最近在複習k r,上面有所介紹,因此花了些時間仔細研究了一下。畢竟把題目做出來是次要的,了解實...

動態記憶體分配 malloc,free 函式分析

c語言中的一切操作都是基於記憶體的。變數和陣列都是記憶體的別名,如何分配這些記憶體由編 譯器在編譯期間決定。定義陣列的時候必須指定陣列長度。而陣列長度是在編譯期就必須決定的 但是實際中有需求 程式執行的過程中,可能需要使用一些額外的記憶體空間。因此就需要採用動態記憶體分配。malloc和free用於...

malloc free記憶體碎片的產生原因

malloc和free大量使用後回造成記憶體碎片,那麼這種碎片形成的機理是什麼?如果機理是申請的記憶體空間大小 太小 所形成的,那麼申請多大的區域能夠最大限度的避免記憶體碎片呢 這裡的避免不是絕對的避免,只是一種概率 記憶體碎片一般是由於空閒的連續空間比要申請的空間小,導致這些小記憶體塊不能被利用....