关于malloc()内存分配的原理

1.什么是虚拟内存?

在说malloc之前要先说清楚什么是虚拟内存,它的意义。

单片机大家都不陌生,它在运行程序时,cpu是直接操作物理内存的地址对数据进行处理。此时,如果有另一个程序要对该地址进行访问则会失败,这就无法进行多线程的并发。这时候,虚拟内存的意义就出现了。

虚拟内存:操作系统会为每个进程都分配好独立的虚拟地址,每个进程都在自己的那一套虚拟地址里操作就好,互不干涉。虚拟内存会通过映射连接到物理内存中,进行相应的读写操作。

2.malloc是如何分配内存的?

需要知道的是,malloc()是c库里的函数,进行动态分配内存。

malloc申请内存的时候,会有两个方式向操作系统申请堆内存。

  • 通过brk()系统调用从堆分配内存
  • 通过mmap()系统调用在文件映射区域分配内存;

brk()的方式比较简单易懂,该函数将堆顶的指针向高地址进行移动,从而开辟新的内存空间。如下图:

mmap()则是通过私有匿名映射的方式,在文件映射区分配一块内存,或者说是从文件映射区创建了一块内存,如图:

那么在什么情况下malloc()会通过brk()或者mmap()分配内存呢?

在malloc()的源码中,定义了一个阈值:

  • 当用户分配的内存小于128KB时,会通过brk()申请内存;
  • 当用户分配的内存大于128KB时,会通过mmap()申请内存;

需要注意的是,不同的glibc版本定义的阈值也不同。

 3.malloc()分配的是物理内存吗?

不是,分配的是虚拟内存。

前文提高过什么是虚拟内存,当malloc分配的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用到物理内存。

当访问到已分配的虚拟内存时,操作系统会查找页表,找到虚拟内存对应的页没有在物理内存中,则会触发缺页中断,之后操作系统会重建虚拟内存和物理内存之间的映射关系。

4.malloc(1)会分配多大的虚拟内存?

malloc()在分配内存时,会预分配更大的内存作为内存池,所以malloc(1)不会按部就班的分配预期申请的内存大小。

那么具体会预分配多大的空间呢?这个malloc使用的内存管理器相关,下面以malloc默认的内存管理器(Ptmalloc2)来分析。

当通过下面代码来申请1字节的内存时,看看操作系统实际分配了多大的内存空间。

void *addr = malloc(1)
printf("此1字节的内存起始地址:%x\n", addr)
printf("使用cat /proc/%d/maps查看内存分配\n",getpid());

这个例子分配的内存小于128KB,所以是通过brk()系统调用向堆空间申请的内存。

输出的堆空间地址范围是00d73000-00d94000,这个范围是132KB,说明malloc(1)实际上预分配了132KB字节的内存空间。

5.当free时,会将内存归还给操作系统吗?

当我们把前面malloc(1)申请的内存通过free()函数释放后,在堆内存开辟的内存还会在吗?

先说结论,在的。因为相比于把这1字节释放给系统,不如缓存在malloc的内存池里,当再次有进程申请1字节的内存时就可以直接复用,提高了一定的性能。不过,当该进程结束时,系统会回收进程下的所有资源。

值得一提的是,上面的情况是malloc通过brk()的方式申请内存,但如果通过mmap()方式申请内存的话,free后的内存会直接归还给操作系统。

总结:

  • malloc通过brk()方式申请的内存,在free释放时,并不会立刻把内存归还给操作系统,而是缓存在malloc的内存池中,以备下次使用;
  • malloc通过mmap()方式申请的内存,在free释放时,会直接把内存归还给操作系统,内存得到即刻释放.

6.为什么不都通过mmap来分配内存?

当进程向操作系统申请内存时,需要通过系统调用,执行系统调用要进入到内核态后再回到用户态,这种运行态的切换会耗费一定的时间。

所以为了性能方面的考虑,申请内存时应当避免频繁的系统调用,如果每次都通过mmap来分配内存,相当于每次都执行系统调用,大大增加了负担。还有,mmap每次释放内存时,都是直接还给操作系统,所以释放后mmap分配的虚拟地址都是缺页状态,这时访问该虚拟地址时,会触发缺页中断,这样会导致cpu消耗较大。

为了改进这个问题,通过brk()来申请在堆空间申请内存。由于堆空间是连续的,所以可以直接预分配更大的内存来作为内存池,当释放内存时,直接缓存在内存池中就好了,等待下次调用。

7.既然如此,为什么不全部用brk来分配?

设想这样一个场景,当我连续申请了10k,20k,30k这三片内存,如果10k和20k都释放了,成为空闲内存空间,当下次申请的内存小于30k时,就可以重用这些空闲内存空间。如图:

但如果后面申请的空间都大于20k,则会不断产生多余的内存碎片,导致类似内存泄露的情况发生,并且这种情况是使用valgrind无法检测出来的。

关于malloc进行内存分配的一些记录就到这,欢迎大佬们指导交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值