kswapd0 吃满 100% CPU,free -h 却显示 100GB+ 可用——NUMA zone_reclaim_mode 内核代码级根因与解决方案
kswapd0 吃满 100% CPU,free -h 却显示 available 还有 100GB+——Linux NUMA zone_reclaim_mode 引发的 direct reclaim 风暴:内核代码级根因与解决方案搬运并适配自国外社区真实案例、LWN.net 内核开发讨论及 Red Hat 知识库。原文出处见文末。TL;DR在双路/多路 NUMA 服务器上,如果vm.zone_reclaim_mode被设置为非零值(某些 BIOS/内核版本可能默认开启),Linux 会在单个 NUMA 节点内存紧张时拒绝使用远程节点的空闲内存,转而触发 direct reclaim(直接回收)——即使free -h显示全局还有 100GB+ 的 available 内存。这会导致 kswapd 吃满 CPU、应用程序 P99 延迟飙升数十倍、系统吞吐量雪崩式下降。2025 年 12 月 Linux 内核社区已正式提交 RFC 补丁废弃该参数(删除 466 行代码,只保留 9 行)。本文从内核页分配器代码路径出发,解释这个机制为什么"actively harmful"。现象生产环境一台双路 Xeon Gold 服务器,256GB 内存(每路 128GB),跑 PostgreSQL + Redis:free -h显示 available 还有 140GB,负载很低但top显示kswapd0吃满一个 CPU 核心的100% sys 时间应用 P99 延迟从 3ms 飙升到800ms+系统整体 CPU sys 使用率飙升到 80%+echo 3 /proc/sys/vm/drop_caches可以暂时缓解,但过一段时间问题重现持续时间从几分钟到半小时不等,然后自动恢复最诡异的是:没有任何进程实际使用那么多内存,free 内存充裕,系统却在疯狂做内存回收。排查过程第一回合:常规检查——颠覆认知$free-htotal usedfreeshared buff/cache available Mem: 251Gi 98Gi 16Gi1.2Gi 137Gi 140Gi Swap: 0B 0B 0Bavailable 有 140GB,swap 没开。没有 OOM killer 事件。但kswapd0在 top 里稳定占据 100% CPU。第二回合:看 /proc/vmstat——发现问题$grep-E'pgscan|pgsteal|allocstall|zone_reclaim'/proc/vmstat pgscan_kswapd12843017pgscan_direct89421156# ⚠️ direct reclaim 扫描了 8900 万页!pgsteal_kswapd9842031pgsteal_direct67120456# ⚠️ direct reclaim 回收了 6700 万页!allocstall34321# ⚠️ 分配停滞 34000+ 次zone_reclaim_success0zone_reclaim_failed28904# ⚠️ zone reclaim 失败 28000+ 次关键信号:pgscan_direct远远大于pgscan_kswapd——说明系统在走 direct reclaim 路径,而不是正常的 kswapd 后台回收allocstall很高——每次allocstall意味着有一个进程在分配内存时被阻塞,等待内核回收页面zone_reclaim_failed很大——zone reclaim 被尝试了大量次数但都失败了第三回合:检查 NUMA 拓扑$ numactl--hardwareavailable:2nodes(0-1)node0cpus:012345678910111213141516171819node0size:128917MBnode0free:1423MB# ⚠️ Node 0 只剩 1.4GB!node1cpus:20212223242526272829303132