【C语言】一文详解 malloc / free 分配内存和释放内存相关问题

目录

malloc 如何分配内存?

malloc 分配的是物理内存吗?

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

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

free 释放内存,会立即归还给操作系统吗?

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


malloc 如何分配内存?

实际上,malloc 并不是系统调用,而是 C 库里的函数,用于动态分配内存。在 Linux 操作系统中,malloc 会采用两种方式向操作系统申请堆内存。

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

方式一实现的方式很简单,就是通过 brk() 函数将堆顶指针向高地址移动,获得新的内存空间。如下图:

 方式二则是通过 mmap 系统调用中私有匿名映射的方式,在文件映射区分配一块内存,如下图:

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

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

  • 如果用户分配的内存小于 128 KB,则通过 brk() 申请内存;
  • 如果用户分配的内存大于 128 KB,则通过 mmap() 申请内存;

看看 man 手册怎么说

Normally, malloc() allocates memory from the heap, and adjusts the size of the heap as required, using sbrk(2). When allocating blocks of memory larger than MMAP_THRESHOLD bytes, the glibc malloc() implementation allocates the memory as a private anonymous mapping using mmap(2).MMAP_THRESHOLD is 128 kB by default, but is adjustable using mallopt(3). Prior to Linux 4.7 allocations performed using mmap(2) were unaffected by the RLIMIT_DATA resource limit; since Linux 4.7, this limit is also enforced for allocations performed using mmap(2).

通常,malloc() 从堆中分配内存,并使用 sbrk(2) 根据需要调整堆的大小。分配内存块时大于MMAP_THRESHOLD字节,glibcmalloc() 实现使用 mmap(2)将内存分配为私有匿名映射。MMAP_THRESHOLD 默认为128 kB,但可以使用 mallopt(3) 进行调整。在Linux 4.7之前,使用 mmap(2) 执行的分配不受影响。RLIMIT_DATA资源限制;自Linux 4.7以来,对于使用mmap(2)执行的分配也强制执行了此限制。

malloc 分配的是物理内存吗?

malloc() 分配的是虚拟内存。如果分配后的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用物理内存了。

只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发缺页中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。

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

因为向操作系统申请内存,是要通过系统调用的,执行系统调用是要进入内核态的,然后在回到用户态,运行态的切换会耗费不少时间。

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

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

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

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

等下次在申请内存的时候,就直接从内存池取出对应的内存块就行了,而且可能这个内存块的虚拟地址与物理地址的映射关系还存在,这样不仅减少了系统调用的次数,也减少了缺页中断的次数,这将大大降低 CPU 的消耗

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

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

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

但是如果下次申请的内存大于 30k,没有可用的空闲内存空间,必须向 OS 申请,实际使用内存继续增大。

因此,随着系统频繁地 malloc(brk) 和 free,尤其对于小块内存,堆内将产生越来越多不可用的碎片。

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

free 释放内存,会立即归还给操作系统吗?

  • malloc 通过 brk() 方式申请的内存,free 释放内存的时候,并不会把内存归还给操作系统,而是缓存在 malloc 的内存池中,下次申请内存时若大小符合,将直接分配这段此前释放掉的内存,以提高内存申请的效率
  • malloc 通过 mmap() 方式申请的内存,free 释放内存的时候,会把内存归还给操作系统,内存得到真正的释放

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

使用 malloc 申请内存时,会在 malloc 返回的地址之前分配一部分内存用来存放内存块的描述信息,里面包含该内存块的大小等。

图片

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

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

毕瞿三谲丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值