Linux 内存管理实践:伙伴系统、slab、mmap 与 OOM
Linux 把教材模型工程化为三层:伙伴系统(buddy allocator)管理物理页框、slab 分配器在其上服务小对象、mmap/brk 为进程构建虚拟区间(VMA),并用 OOM killer 作为内存耗尽的最后防线。
一句话定义
Linux 把教材模型工程化为三层:伙伴系统(buddy allocator)管理物理页框、slab 分配器在其上服务小对象、mmap/brk 为进程构建虚拟区间(VMA),并用 OOM killer 作为内存耗尽的最后防线。
为什么重要
教材讲原理,运维与开发每天面对的是这套真实机器:free 输出怎么读、Java 服务为什么 RSS 缓慢爬升不回落、OOM 杀进程为什么"看起来随机"。理解本节,能把 kp-016~kp-018 的模型对应到 /proc 里的每一个字段,这是性能观测(kp-031)的内存部分基本功。
前置知识
kp-018(缺页、匿名页与文件页);kp-017(页与帧)。
核心概念
- 伙伴系统:以 2 的幂(order 0~10,即 4KB~4MB)管理页框,分配时找最小可用块、对半分裂,释放时递归合并"伙伴"。
- slab/slabtop:内核小对象(task_struct、inode 等)的对象缓存,按类型预分配整页切成等大小对象,避免内部碎片与反复构造。
- VMA(virtual memory area):进程地址空间的连续区间描述(代码/堆/栈/文件映射),
/proc/pid/maps就是 VMA 列表。 - mmap 的两种用途:文件映射(页缓存直接映射进地址空间)与匿名映射(大块堆、线程栈)。
- RSS/VSS:驻留集(真实占用的物理页)vs 虚拟空间大小。
- OOM killer:内存与 swap 耗尽时按
badness评分选择牺牲者(默认偏向内存大、OOM 评分高的进程)。
原理与机制
分层结构:
用户: malloc/free (glibc ptmalloc, 自带小块缓存与竞技场)
|-- 小块(<128KB): brk/mmap 扩展堆, 空闲链表管理, 释放常不归还OS
|-- 大块: 直接 mmap, munmap 立即归还
内核: VMA 记录区间 -> 访问时缺页 -> 伙伴系统分配页框
|-- 内核自身小对象: slab 缓存 (kmalloc 底层)
回收: kswapd 后台 + 直接回收, 按 swappiness 倾向换出匿名页/丢弃干净文件页
最后: 内存+swap 全耗尽 -> OOM killer 评分选杀
两个最反直觉的事实:malloc 成功 ≠ 物理内存到手(只登记 VMA,首次写入才缺页分配——惰性分配,calloc 的大块甚至先给共享的零页);free() 后 RSS 常不下降(glibc 把小块留在空闲链表复用,glibc arenas 默认按核数增长,多线程 Java/C 服务 RSS 爬升的常见根因,MALLOC_ARENA_MAX 可调)。
直观类比
伙伴系统像只备标准规格包装纸的礼品店:任何需求都拆分成 2 的幂组合,退回的纸只要原尺寸相邻就能拼回大张。slab 像快餐店的餐盘架:盘子(对象)洗好码放,取用归还都是 O(1),不为每个客人现造盘子。
实例或案例
free -h # available 才是"可用内存", free 不含可回收缓存
cat /proc/buddyinfo # 每个订单(2^order)的空闲块数, 连续大页紧缺时高阶为0
slabtop # 内核 slab 对象实时排行(dentry/inode 缓存常居首)
strace -e trace=mmap,munmap,brk ./a.out # 观察分配路径
grep -i oom /var/log/syslog 或 journalctl -k | grep -i oom # OOM 事件取证
实验:malloc(1GB) 后仅 memset 一半,/proc/pid/smaps_rollup 中 Rss 只涨一半——惰性分配的直接证据。
常见误区
- 读
free只看 free 列:buff/cache 可被回收,available 才接近真实余量。 - 认为 OOM 是内核 bug:它是资源耗尽的策略性止损,正确响应是查泄漏或加资源限额(cgroup,见 kp-029)。
- 混淆 VSZ 与 RSS:VSZ 是登记的虚拟空间,RSS 才是实际占用;容器内存限额监控必须用 RSS/cgroup 计数。
与其他知识点的关系
伙伴系统是 kp-016 分配策略的工程答案;slab 服务 kp-005 的 task_struct 等对象;cgroup 内存限额(kp-029)触发的是同一套回收与 OOM 逻辑。
延伸阅读
《深入理解 Linux 内核》第 8 章;内核文档 Documentation/admin-guide/mm。
自测题
- malloc 成功但物理内存没变化,为什么?
答:分配器只在虚拟地址空间登记 VMA;首次写入才触发缺页,由伙伴系统分配真实页框,即惰性(按需)分配。
- 伙伴系统如何解决外碎片?
答:所有块为 2 的幂,分裂与合并保证任意相邻同阶空闲块总可合并回更大块,大块永远不会被切碎到无法重组。
- 多线程服务 RSS 缓慢爬升的可能原因?
答:glibc 每核一个 malloc arena 的默认策略使线程各自持有空闲内存不归还;可通过 MALLOC_ARENA_MAX、或换 jemalloc/tcmalloc、或大块改用 mmap 分配缓解。