石家莊電子商城網(wǎng)站建設(shè)鄭州網(wǎng)站關(guān)鍵詞排名
? ? ? ?2021年末面試蔚來汽車,面試官考察了malloc/free的實現(xiàn)機制。當時看過相關(guān)的文章,有一點印象,稍微說了一點東西,不過自己感到不滿意。今天嘗試研究malloc的實現(xiàn)細節(jié),看了幾篇博文,發(fā)現(xiàn)眾說紛紜,且實現(xiàn)比較復雜。在此,對malloc的實現(xiàn)機制進行研究,了解其大概的工作機制,沒有深究細節(jié)。
先說結(jié)論:
內(nèi)存的分配:
可以把內(nèi)存空間的分配分為兩個過程:
1)從操作系統(tǒng)獲取到內(nèi)存,實際獲取到的內(nèi)存一般比進程申請的空間大一些。
2)從申請到的空間中拿出來一部分給到進程。
所以,一般會有部分備用的內(nèi)存空間。所以,就有了下面的結(jié)論。
1.如果進程申請的空間小于備用的空間,則直接把內(nèi)存分配給進程。
2.如果進程申請的空間大于備用的空間,
1)申請的空間<128KB,通過系統(tǒng)調(diào)用brk在現(xiàn)有地址的基礎(chǔ)上擴張,即新分配的空間和之前的空間是連續(xù)的。
2)申請的空間>128KB,通過mmap在物理內(nèi)存映射一段空間。
內(nèi)存的釋放:
試驗環(huán)境:ubuntu虛擬機,gcc:Ubuntu 9.4.0-1ubuntu1~20.04.1
測試代碼:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>typedef unsigned char uint8;int main()
{//step1getchar();uint8* p1 = (uint8*)malloc(127 * 1024 * sizeof(uint8));printf("p1:%p,size:%ld\n", p1, malloc_usable_size(p1));//step2getchar();free(p1);//step3getchar();uint8* p2 = (uint8*)malloc(127 * 1024 * sizeof(uint8));printf("p2:%p,size:%ld\n", p2, malloc_usable_size(p2));//step4getchar();uint8* p3 = (uint8*)malloc(127 * 1024 * sizeof(uint8));printf("p3:%p,size:%ld\n", p3, malloc_usable_size(p2));//step5getchar();uint8* p4 = (uint8*)malloc(500 * 1024 * sizeof(uint8));printf("p4:%p,size:%ld\n", p4, malloc_usable_size(p4));//step6getchar();uint8* p5 = (uint8*)malloc(1024 * sizeof(uint8));printf("p5:%p,size:%ld\n", p5, malloc_usable_size(p5));//step7getchar();free(p5);//step8getchar();free(p4);getchar();return 0;
}
測試方法:
1.通過strace跟蹤進程執(zhí)行的過程。
2.通過cat /proc/PID/maps觀察堆區(qū)的分配情況。
測試過程和分析:
strace ./a.out
略
brk(NULL) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? = 0x55c726c6e000
brk(0x55c726c8f000) ? ? ? ? ? ? ?= 0x55c726c8f000
//進程啟動之后,系統(tǒng)自動分配了0x55c726c8f000-0x55c726c6e000,約為400K的內(nèi)存,這部分內(nèi)存,是備用的。
//step1:55c726c6e000-55c726c8f000 [heap]
//進程請求分配127K內(nèi)存,由于系統(tǒng)已有400K空余的內(nèi)存,夠用,所以,直接給進程返回一塊內(nèi)存。起始地址為p1=0x55c726c6e6b0,和預留的起始地址相比,有一定的偏移量
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(1, "p1:0x55c726c6e6b0,size:130056\n", 30p1:0x55c726c6e6b0,size:130056
) = 30
//step2:55c726c6e000-55c726c8f000 [heap]
//free(p1),系統(tǒng)只是把這塊空間標記為可用的,并沒有真正的釋放其內(nèi)存空間。
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
//step3:55c726c6e000-55c726c8f000 [heap]
//再次請求分配127K的內(nèi)存,備用的內(nèi)存空間夠用,同step1,直接分配給進程。
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
write(1, "p2:0x55c726c6e6b0,size:130056\n", 30p2:0x55c726c6e6b0,size:130056
) = 30
//step4:55c726c6e000-55c726ccf000 [heap]
//再次請求分配129K的內(nèi)存空間,之前剩余的備用空間約為400K-127K-127K=146K,不夠用了。而且,請求分配的內(nèi)存<128K,在原來地址的基礎(chǔ)上,通過brk擴充內(nèi)存空間,即和原來的內(nèi)存是連續(xù)的
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
brk(0x55c726ccf000) ? ? ? ? ? ? ? ? ? ? = 0x55c726ccf000
write(1, "p3:0x55c726c8e6d0,size:130056\n", 30p3:0x55c726c8e6d0,size:130056
) = 30
//step5
//55c726c6e000-55c726ccf000 [heap]
//f2b3313f000-7f2b331bd000?
//再次請求分配500K的內(nèi)存空間,之前剩余的備用空間不夠用了,而且,請求分配的內(nèi)存>128K,通過mmap在物理內(nèi)存映射一塊空間,且和原來的空間是不連續(xù)的
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
mmap(NULL, 516096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2b3313f000
write(1, "p4:0x7f2b3313f010,size:516080\n", 30p4:0x7f2b3313f010,size:516080
) = 28
//step6
//55c726c6e000-55c726ccf000 [heap]
//f2b3313f000-7f2b331bd000?
//同step1
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
<pre>write(1, "p5:0x55c726cae2e0,size:1032\n", 28p5:0x55c726cae2e0,size:1032</pre>
//step7
//55c726c6e000-55c726ccf000 [heap]
//f2b3313f000-7f2b331bd000?
//同step2
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
//step8:55c726c6e000-55c726ccf000 [heap]
//通過munmap釋放內(nèi)存
read(0,?
"\n", 1024) ? ? ? ? ? ? ? ? ? ? = 1
munmap(0x7f2b3313f000, 516096)