Note

malloc 底层用什么系统调用?大小内存一样吗?

说明 glibc 等常见实现里小块走 brk/sbrk 堆扩展、大块常走 mmap,以及阈值策略差异。

省流

malloc 本身是 C 库中的用户态分配器,不是内核系统调用。真正向操作系统申请虚拟地址空间时,常见路径主要是 brk/sbrk 和 mmap。二者不会在所有大小上无差别地使用同一个策略。

以 glibc 的 ptmalloc 一类实现为例:较小的分配通常在进程堆上管理,堆通过 brk/sbrk 上下移动 program break 来扩展或收缩;较大的分配常常直接 mmap 匿名映射,释放时再用 munmap 归还。分界阈值由实现决定,历史上常见量级是数十到数百 KB,且可配置,不能背成一个永远不变的常数。

这样拆分的原因是:小对象分配极频繁,适合在用户态用 freelist/bin 复用,减少系统调用;大对象若长期留在堆中间,容易造成堆难以收缩和碎片问题,独立映射更利于整块归还。

分层关系

应用:malloc / free
        |
        v
C 库分配器(如 ptmalloc):管理 arena、chunk、bin
        |
        +-- 小块:扩展/使用主堆  -> brk / sbrk
        +-- 大块:匿名映射      -> mmap / munmap

面试时要先说清:malloc 多数时候只是在已向内核要过的虚拟内存里做切分与复用;只有现有空间不够,或策略要求独立映射时,才会进入系统调用。

brk / sbrk

  • 调整进程 data segment 末尾,即 program break。
  • 适合作为“一块可伸展的堆”的底层来源。
  • free 小块后,内存往往先回到分配器,不一定立刻把 break 降回去;只有高地址连续空闲到一定程度,才可能收缩堆。

mmap

  • 可映射文件,也可做匿名映射(Anonymous Mapping)给内存分配用。
  • 大块 malloc 走 mmap 时,对应 free 通常可直接 munmap,地址空间立刻还给内核。
  • 也用于文件映射、共享内存等,不只服务 malloc。

大小内存是否用同一函数?

对应用来说都是 malloc;对操作系统来说,底层系统调用路径可以不同。

典型情况更常见的内核接口释放时
小分配复用堆;必要时 brk/sbrk回到用户态 freelist,未必立刻归还内核
大分配mmap 匿名页常直接 munmap

具体阈值、是否启用 mmap、多线程 arena 细节因 libc 版本与编译选项而异。回答重点是机制,而不是背某一个魔法数字。

补充

  • 即使用 mmap 得到虚拟地址,物理页往往仍按缺页中断按需分配。
  • 其他分配器(jemalloc、tcmalloc、musl malloc 等)同样是“用户态策略 + 少量系统调用”,但切分阈值和数据结构不同。
  • 直接调用 mmap/brk 可以自己做分配器;日常业务代码仍应优先使用库提供的 malloc/free 或语言运行时。