linux内存碎片本质是物理内存割裂导致大块连续请求失败,分外部(空闲页框不连续)和内部(分配粒度刚性造成边角料)两类,需通过内存池、内存整理、cgroup预留等手段缓解。

Linux内存碎片问题本质是物理内存空间被割裂或浪费,导致大块连续内存请求失败,即使总空闲量充足。它不等于内存不足,也不等同于内存泄漏,而是一种“有空间却用不上”的结构性低效。
内存碎片的两种类型与根源
碎片分内外两类,成因和表现完全不同:
-
外部碎片:空闲页框物理上不连续,无法拼出高阶(如 order=3 即 32KB)连续内存。典型诱因是频繁小块分配/释放(如 malloc(1KB)/free() 交替)、进程堆动态伸缩、内核对象零散回收。查看命令:
cat /proc/buddyinfo中高阶(order≥4)列数值持续为 0,即预警信号。 - 内部碎片:已分配内存块中未被使用的“边角料”。主因是分配粒度刚性——页大小固定为 4KB(申请 5KB 就得占 8KB),或 SLUB 分配器按 slab 缓存块对齐(如对象 23 字节,分配 32 字节块,浪费 9 字节)。这类碎片无法被其他请求复用,纯属静态浪费。
哪些行为会加速碎片化
以下操作在长期运行服务中尤为危险:
- 用户态程序反复调用
malloc/free不同尺寸内存(尤其 64B–2KB 区间),且生命周期错乱(早分配晚释放、或反之); - 内核模块频繁申请
kmalloc小内存(如网络驱动每包分配 skb buffer),又未使用 per-CPU 缓存或 mempool; - 禁用透明大页(THP)且应用未主动使用
mmap(MAP_HUGETLB),导致大量 4KB 页堆积,加剧伙伴系统压力; - 长时间运行未重启的 Java 或 .NET 进程,JVM GC 虽回收逻辑内存,但 native 堆(如 DirectByteBuffer)释放后仍可能留下物理页碎片。
缓解内存碎片的实用手段
不能根除,但可显著抑制其影响:
- 应用层:改用内存池(如 jemalloc、tcmalloc)替代默认 glibc malloc,它们通过 arena 管理和 slab 复用大幅减少外部碎片;
- 内核层:启用
/proc/sys/vm/compact_memory手动触发内存整理(适合维护窗口),或设vm.extfrag_threshold调整内核自动整理触发阈值; - 部署层:为关键服务预留
cgroup v2 memory.min,避免被 OOM killer 误杀;对 DMA 密集型设备,通过kernel boot param: mem=限制低端内存范围,隔离碎片敏感区; - 监控层:将
/proc/buddyinfo和/proc/pagetypeinfo纳入 Prometheus 采集,对 order≥5 的空闲页数设置告警(低于 5 即需干预)。
误区澄清:什么不是碎片问题
遇到内存紧张时,先排除常见混淆项:
- 显示
available很低 ≠ 碎片 —— 可能只是 page cache 占用高,echo 1 > /proc/sys/vm/drop_caches后恢复; - OOM Killer 触发 ≠ 一定是碎片 —— 更常见原因是整体内存耗尽或
overcommit策略激进(vm.overcommit_memory=2且vm.overcommit_ratio设置过低); -
slabinfo中大量dentry或inode缓存 ≠ 外部碎片 —— 这属于可回收内核缓存,echo 2 > /proc/sys/vm/drop_caches即可清理。










