Linux 面试题:malloc 和 free 背后的 brk、mmap 与内存分配器

在 Linux 系统编程的面试中,mallocfree 几乎是必考知识点。很多候选人能说出“malloc 分配堆内存,free 释放内存”,但当面试官追问“malloc 底层是怎么向操作系统要内存的?”、“为什么 free 之后内存没有立刻还给系统?”、“brk 和 mmap 有什么区别?”时,回答往往就开始模糊了。

这篇文章从一道常见的面试题出发,把 malloc/freebrk/mmap 以及 glibc 内存分配器之间的关系讲清楚。

一、先厘清层次:malloc 不是系统调用

这是最容易被误解的一点。mallocfreeC 标准库(glibc) 提供的函数,而不是 Linux 系统调用。它们运行在用户态,负责管理进程的**堆(heap)**内存。

真正向内核申请内存的系统调用主要有两个:

  • brk / sbrk:调整进程堆顶指针(program break)。
  • mmap / munmap:在进程地址空间中建立或删除一段映射。

所以完整的调用链大致是:

应用程序 → malloc/free(glibc 分配器) → brk/mmap(系统调用) → 内核内存管理

理解这条链,是回答所有相关面试题的基础。

二、brk:传统的堆扩展方式

brk 系统调用通过移动“program break”来扩展或收缩堆区。program break 是数据段(data segment)末尾之后的第一个地址,也就是堆的边界。

  • malloc 需要更多堆内存时,分配器可以调用 brk 把边界往上推,从而获得新的内存。
  • 当释放内存时,理论上可以调用 brk 把边界往下拉,把内存还给内核。

brk 的特点:

  • 连续:堆是一整块连续的内存区域。
  • 适合小内存:分配小对象时开销低。
  • 释放受限:只有当堆顶部的内存被释放后,才能收缩边界。如果中间有块内存没释放,堆顶就无法下降,容易产生“碎片化”导致内存无法归还。

正因为这个限制,glibc 分配器对 brk 的使用非常“保守”——它通常不会频繁地把内存还给内核。

三、mmap:大块内存与独立映射

mmap 把一个文件或匿名内存区域映射到进程地址空间。malloc 在分配较大内存块时,会直接使用 mmap(匿名映射,即 MAP_ANONYMOUS | MAP_PRIVATE)。

mmap 的特点:

  • 独立:每次映射是一块独立的区域,与堆无关。
  • 适合大内存:大块分配用 mmap 更高效,避免堆碎片。
  • 释放直接free 时通过 munmap 直接归还给内核,内存立即释放。

glibc 有一个阈值 M_MMAP_THRESHOLD(默认约 128KB,且会动态调整)。超过这个阈值的分配走 mmap,低于它的走 brk 堆。

四、glibc 内存分配器做了什么

如果每次 malloc 都直接调用系统调用,性能会非常差(系统调用涉及用户态/内核态切换)。所以 glibc 的分配器(ptmalloc)在用户态做了一层缓存和管理:

  1. 维护空闲链表(bins):释放的内存不会立刻还给内核,而是放进空闲链表,供后续 malloc 复用。
  2. 区分大小:小内存走堆(brk),大内存走 mmap。
  3. 延迟归还:只有堆顶部有足够大的连续空闲块时,才可能调用 brk 收缩;mmap 的内存通常直接 munmap

这解释了一个经典现象:程序 free 了内存,但 RSS(常驻内存)没有下降。因为内存还在分配器的空闲链表里,并没有真正还给操作系统。

五、高频面试问答

Q1:malloc 分配的内存是物理内存吗?

不是。malloc 返回的是虚拟地址,此时只是建立了虚拟地址空间的映射(甚至可能只是分配器内部记账)。真正的物理页在首次访问时才通过缺页异常分配,这就是“延迟分配(lazy allocation)”。所以 malloc 一大块内存后立刻 free,可能根本没占用物理内存。

Q2:brk 和 mmap 如何选择?

由 glibc 根据分配大小自动决定:小于阈值(默认约 128KB)用 brk,大于阈值用 mmap。阈值会随程序行为动态调整。

Q3:为什么 free 后内存没还给系统?

因为 glibc 把释放的内存放进了空闲链表缓存起来,留给后续 malloc 复用。这是性能优化,不是 bug。若想强制归还,可调用 malloc_trim(0)

Q4:什么是内存碎片?

分内部碎片(分配器对齐或块头造成的浪费)和外部碎片(空闲内存分散、无法满足大块连续请求)。brk 堆尤其容易产生外部碎片。

Q5:多线程下 malloc 安全吗?

安全。glibc 使用 arena 机制,每个线程可以有自己的 arena,减少锁竞争,但跨 arena 的分配仍有开销。

六、总结

回答这类面试题,关键是建立清晰的分层认知:

  • 应用层:调用 malloc/free
  • 库层:glibc ptmalloc 管理空闲链表、决定用 brk 还是 mmap。
  • 内核层brk/mmap 系统调用建立虚拟内存映射,物理内存延迟分配。

把“malloc 不是系统调用”“brk 管小块、mmap 管大块”“free 不等于归还内核”这几点讲透,基本就能覆盖面试官想考察的核心。

未经允许不得转载:任鹏个人博客 » Linux 面试题:malloc 和 free 背后的 brk、mmap 与内存分配器

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏