为什么free()时不需要传指针大小
malloc()和free()是c中兩個非?;镜暮瘮?#xff0c;但這種最基本的東西往往都是特別復雜的。
malloc和free的原形如下:
void *malloc(unsigned int num_bytes);
void free(void *ptr);
在c的標準中并沒有定義這兩個函數的具體實現,在我們最常用的gcc中,malloc使用的是ptmalloc的實現,最早由Doug Lea完成,并被Wolfram Gloger改進以支持多線程。
malloc的具體實現過于復雜,這里就不具體展開了。
總體上說,ptmalloc的內存管理是基于內存池的,而它的內存來源有兩種:
1 通過brk()獲得
2 通過mmap()匿名映射獲得
這兩種獲得內存的方式分別對應于進程地址空間的不同部分。64位linux進程的默認內存布局如下:
對于其中的Heap區域,就是我們通常意義上的堆空間,這部分內存將通過brk()獲得。brk()的原理非常簡單,只涉及指針的上下移動,也就是只分配虛擬地址空間,而不分配物理內存,當實際訪問到此內存時,將觸發page fault異常由操作系統完成物理內存的分配。由于操作簡單,所以brk的效率非常高。
而另一個Memory Mapping Region區域,簡稱mmap區域,將通過操作系統的mmap()函數獲得。mmap區域不僅可以提供內存的分配,還可以映射文件,比如通過mmap打開文件,或者加載so文件等等。
mmap函數的原形如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
其中的flags,在使用mmap分配內存時,設置為MAP_ANONYMOUS,也就是匿名映射。當使用mmap()映射內存時,操作系統默認將對這塊內存執行清0操作,因此相比于brk,mmap的效率相對較低。
基于內存池的內存管理,本質上都是把變長的內存分配轉換為定長的內存分配。因為只有定長,才可以復用。
ptmalloc的內存池結構大致如下:
可以看出,內存的分配將根據實際的大小,選擇定長塊。比如請求的內存是23字節,那么將分配一塊24字節的內存,而請求的如果是25字節,那么將分配一塊32字節的內存(對于大于512字節的數據,統一存儲在large bin中)。具體的分配策略就不展開了。
每一種大小的內存,都組成了一個一個的隊列,在這些隊列里維護了一個雙向鏈表,將所有的小塊內存串聯起來。每一個小塊內存(chunk)的結構如下:
上面的這個結構是一塊在使用中的內存的狀態。其中的mem部分,則是返回給用戶的void*指針位置。而最開始的兩個結構:size of previous chunk,和 size of chunk,則用于維護全局內存的鏈表。
之所以說是全局內存的鏈表,是由于內存分配時是先向系統請求一個比較大的內存塊(64位系統一般為64Mb),之后從這64Mb內存中切出用戶需要的大小分配給用戶。而為了維護分配出去的內存塊之間的關系,通過前兩個結構來使所有內存塊構成一個大的鏈表,當回收內存時,通過這個全局鏈表,將所有空閑內存組合起來,還給操作系統,其中有3個標記位:A,M,P,P標識前一塊內存是否空閑。
下面的結構就是一塊空閑內存的狀態:
相比于使用中的狀態,空閑部分的內存增加了4個新的結構(Forward pointer to next chunk in list 等,其中fd_nextsize和bk
_nextsize只存在于large bin中),這4個結構用于維護每個定長內存隊列的雙向鏈表結構,這個鏈表的存在主要是為了分配時查找內存時足夠便利,可以基本上保證分配內存時的平均復雜度維持在O(1)。
在有了前面所有的介紹之后,可以總體上描述一下malloc和free的基本流程:
當用戶向ptmalloc請求內存時:
1 首先查找定長內存分配池,如果查找到則返回
2 如果沒有空閑內存可供使用,則向操作系統申請一塊64Mb的內存,從中切出用戶需要的內存,返回
當用戶調用free釋放內存時:
1 直接將內存放入適當的定長內存池隊列
2 如果觸發了一定的條件,則將所有空閑內存合并,如果滿足釋放條件,將內存全部還給操作系統
當然了,上面的描述中省略了太多的細節。比如什么時候走brk什么時候走mmap, 再比如當請求的內存大于一個闕值時,ptmalloc將會變成一個mmap的簡單封裝,還有觸發內存歸還操作系統的條件等等。
不過已經足夠回答題目中的問題了:因為malloc的時候記錄了大小。
這里還可以得出另一個結論:由于malloc的時候記錄了大量的狀態,所以在頻繁使用malloc分配小內存時,會造成大量的內存浪費。舉例來說,當反復malloc(1)時,每一次分配的內存在32字節:包括size of previous chunk,size of chunk,bk_chunk_pointer,fd_chunk_pointer共4個指針,合計4 * 8 = 32字節....
作者:littlersmall
鏈接:https://www.jianshu.com/p/871e63168033
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的为什么free()时不需要传指针大小的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: git管理大项目或者大文件
- 下一篇: 海信VIDAA K1G小聚魔盒最新评价怎