malloc 到底在干什么:从 sbrk 到空闲链表一步步写个分配器
来源:benmandrew.com — 2026-11-08
📋 概述
接口极简的 malloc 背后是用户态的记账工作。作者用 strace 展示:连续 malloc(16) 一千次只有两次 brk 系统调用——第一次问程序断点在哪,第二次把堆扩了 132 KiB,其余 999 次都从这块已拿到的内存里切。原因很直接:空系统调用要 575 ns,sbrk(16) 610 ns,而 malloc(16) 只要 26.9 ns,快 22.7 倍。于是他从一个只会 bump 的玩具分配器开始,逐级补上 chunk 头、空闲链表、分割与合并、需求分页、trim 归还内存,最后在 128 KiB 以上改用 mmap。
🔑 核心要点
- chunk 头就藏在返回指针前面 8 字节,大小永远是 16 的倍数所以低 3 位空出来挂标志位;申请 1、24、25、1000 字节分别拿到 32、32、48、1008 字节的 chunk。
- 空闲时那块用户内存被复用成 next 指针,大小加指针正好塞进原数据区,这是 free 不需要额外参数也能串起链表的原因。
- 需求分页解释了为什么 free 之后 RSS 不降:写过的页在 my_free 之后仍然驻留,直到分配器把地址还给内核。
- 只有堆顶能归还:一个活着的 chunk 会把断点钉在上面,它下面的空闲区只能留着,长期运行就变成碎片,把 RSS 顶到被 OOM 杀掉。
- 128 KiB 及以上的请求走 mmap MAP_ANONYMOUS,munmap 可以立刻归还,不受邻居影响,这正对应 glibc 的 MMAP_THRESHOLD。
💡 金句
同样的 16 字节请求,malloc 比 sbrk 快 22.7 倍。
👍 0
👎 0
← 返回 Lobsters 首页