malloc内存分配

  • Post author:
  • Post category:其他


什么场景下 malloc() 会通过 brk() 分配内存?又是什么场景下通过 mmap() 分配内存?

malloc() 源码里默认定义了一个阈值:


如果用户分配的内存

小于 128 KB

,则通过

brk()

申请内存;

如果用户分配的内存

大于 128 KB

,则通过

mmap

() 申请内存;



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

不是的,malloc() 分配的是

虚拟内存


如果分配后的虚拟内存

没有被访问

的话,是不会将虚拟内存

不会映射

到物理内存,这样就不会占用物理内存了。

只有在

访问

已分配的虚拟地址空间的时候,操作系统通过

查找页表

,发现虚拟内存对应的页没有在物理内存中,就会触发

缺页中断

,然后操作系统会

建立

虚拟内存和物理内存之间的

映射关系

对于 「malloc 申请的内存,free 释放内存会归还给操作系统吗?」这个问题,我们可以做个总结了:

malloc 通过

brk()

方式申请的内存,free 释放内存的时候,内存

不会归还

给操作系统,而是

缓存在 malloc 的内存池中,待下次使用;


malloc 通过

mmap()

方式申请的内存,free 释放内存的时候,会把内存

归还

给操作系统,

内存得到真正的释放。


为什么不全部使用 mmap 来分配内存?


因为向操作系统申请内存,是要通过

系统调用

的,

执行系统调用是要进入内核态的,然后在回到用户态,运行态的切换会耗费不少时间。


所以,申请内存的操作应该避免频繁的系统调用,如果都用 mmap 来分配内存,等于每次都要执行系统调用。


另外,因为 mmap 分配的内存每次释放的时候,都会归还给操作系统,于是每次 mmap 分配的虚拟地址都是缺页状态的,然后在第一次访问该虚拟地址的时候,就会触发缺页中断。


也就是说,频繁通过 mmap 分配的内存话,不仅每次都会发生运行态的切换,还会发生缺页中断(在第一次访问虚拟地址后),这样会导致 CPU 消耗较大。


为了改进这两个问题,malloc 通过

brk()

系统调用在堆空间申请内存的时候,由于堆空间是连续的,所以直接预分配更大的内存来作为

内存池



当内存释放的时候,就缓存在内存池中。


等下次在申请内存的时候,就

直接从内存池取出对应的内存块就行了

,而且可能这个内存块的虚拟地址与物理地址的映射关系

还存在



这样不仅减少了系统调用的次数,也减少了缺页中断的次数,

这将大大降低 CPU 的消耗。

既然 brk 那么牛逼,为什么不全部使用 brk 来分配?


前面我们提到通过 brk 从堆空间分配的内存,并不会归还给操作系统,那么我们那考虑这样一个场景。

如果我们连续申请了 10k,20k,30k 这三片内存,如果 10k 和 20k 这两片释放了,变为了空闲内存空间,如果下次申请的内存小于 30k,那么就可以重用这个空闲内存空间。

但是如果下次申请的内存

大于 30k,没有可用的空闲内存空间

,必须向 OS 申请,实际使用内存继续增大。

因此,随着系统频繁地 malloc 和 free ,尤其对于

小块内存,堆内将产生越来越多不可用的碎片,导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。

所以,malloc 实现中,充分考虑了 brk 和 mmap 行为上的差异及优缺点,默认分配大块内存 (128KB) 才使用 mmap 分配内存空间。

free() 函数只传入一个内存地址,为什么能知道要释放多大的内存?


还记得,我前面提到, malloc 返回给用户态的内存起始地址比进程的堆空间起始地址多了 16 字节吗?

这个多出来的 16 字节就是保存了该内存块的描述信息,比如有该内存块的大小。

这样当执行 free() 函数时,free 会对传入进来的内存地址向左偏移 16 字节,然后从这个 16 字节的分析出当前的内存块的大小,自然就知道要释放多大的内存了。

原文地址

malloc 是如何分配内存的?_小林coding的博客-CSDN博客_malloc分配内存