真实存在且可观测:/proc/buddyinfo 显示高阶空闲页为0而低阶堆满、vmstat 中 compact_migrate_scanned 持续增长、extfrag_index 接近1000、Redis mem_fragmentation_ratio>1.5,均证实内外存碎片已实际发生。

真实存在,且对生产系统有明确可观测影响——不是理论问题,而是 /proc/buddyinfo 里能数出来的页块、mem_fragmentation_ratio 里能算出来的比值、compact_migrate_scanned 里能统计到的压缩动作。
看得到的碎片:用 /proc/buddyinfo 验证外部碎片
伙伴系统(Buddy System)把物理内存按 2n 页分组管理,/proc/buddyinfo 直接暴露各阶空闲块数量。如果高阶(如 order=8,对应 1MB)全为 0,而低阶(order=0~3)堆满小块,说明大块连续内存已耗尽。
- 执行
cat /proc/buddyinfo,重点关注 Node 0 Zone Normal 行; - order=10 对应 4MB 块(2¹⁰ × 4KB),若该列长期为 0,但总空闲内存充足(
grep MemFree /proc/meminfo),就是典型外部碎片; - 注意:NUMA 多节点系统要分别检查每个 Node,碎片可能只集中在某一个 zone;
- 别误读“空闲页多=没碎片”——碎片的本质是“不连续”,不是“不够”。
感觉得到的碎片:触发 compact 或 direct reclaim
当内核申请大页失败时,会自动触发内存规整(compact)或直接回收(direct reclaim),这两类事件在 /proc/vmstat 中有明确计数。
- 查
grep -E "compact|pgpgin|pgpgout" /proc/vmstat,持续增长的compact_migrate_scanned或pgmajfault是碎片活跃信号; -
echo 1 > /proc/sys/vm/compact_memory可手动触发一次规整,之后立刻再看/proc/buddyinfo——如果高阶页数量回升,就证实碎片可被整理; - 但要注意:
compact本身开销不小,频繁触发反而加重 CPU 和 TLB 压力; - 若
extfrag_index接近 1000(cat /sys/kernel/debug/extfrag/extfrag_index),说明外部碎片严重,此时reclaim效果差,应优先考虑compact。
应用层的碎片:不只是内核的事
用户态程序的堆内存也会产生严重碎片,尤其在长期运行、频繁 malloc/free 小对象的服务中(如 Redis、Java 应用)。这种碎片不会反映在 /proc/buddyinfo,但会抬高 used_memory_rss / used_memory 比值。
- Redis 的
mem_fragmentation_ratio> 1.5 就值得警惕,> 2.0 往往意味着 glibc malloc 已严重碎片化; - 用
pmap -x $(pidof redis-server)观察 RSS 与 SIZE 差距,大片零散[heap]区域是典型表现; - 替换分配器最有效:
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 redis-server; - 别指望
malloc_trim(0)彻底解决——它只尝试收缩 top chunk,对中间碎片无效。
碎片不是“会不会出现”的问题,而是“什么时候显现、在哪一层显现”的问题。内核层碎片影响大页分配和 DMA,用户层碎片拖慢堆分配并增加 RSS,两者常共存却需分开诊断。最容易被忽略的是:同一台机器上,/proc/buddyinfo 看似正常,但某个 Java 进程的 GC 日志里已频繁出现 “concurrent mode failure”,那正是堆碎片在另一维度咬人。










