理解内核内存回收:水位、kswapd、直接回收与 OOM
内存回收发生在空闲页低于水位时:kswapd 在后台回收,分配路径在压力大时进入直接回收(direct reclaim),仍不能满足就触发 OOM killer。回收对象主要是文件页、匿名页(需要 swap)和可回收 slab;page cache 与 tmpfs 的记账方式不同。
/proc/zoneinfo 的 min/low/high;vm.min_free_kbytes、vm.watermark_scale_factor 影响水位。/proc/vmstat 的 pgscan_kswapd*、pgscan_direct*、pgsteal*、allocstall*、oom_kill。/proc/pressure/memory(PSI,4.20+)的 some/full avg10 比 free 更能反映卡顿。memory.current/max/high/stat/events;较新内核支持 memory.reclaim 主动回收。/proc 统计一般不需要 root;改 sysctl、写 memory.reclaim、触发 SysRq 需要 root。# 查看内存压力(some 表示至少一个任务被拖慢;文件不存在说明内核未启用 CONFIG_PSI)
cat /proc/pressure/memory
# 查看直接回收与 kswapd 扫描量:direct 持续增长说明分配路径在受苦
grep -E 'pgscan_(direct|kswapd)|pgsteal|allocstall|oom_kill' /proc/vmstat
# 查看各 zone 的水位和空闲页
grep -E 'Node [0-9]+, zone|pages free|^ +min |^ +low |^ +high ' /proc/zoneinfo
# 查看可回收 slab 占用
sudo slabtop -o -s c | head -20
# 查看 cgroup 的内存压力、回收和 OOM 计数(v2)
cat /sys/fs/cgroup/<path>/memory.pressure
grep -E 'pgscan|pgsteal|workingset' /sys/fs/cgroup/<path>/memory.stat
cat /sys/fs/cgroup/<path>/memory.events
# 主动回收 1G(cgroup v2,5.19+;先确认内核支持该文件)
echo 1G | sudo tee /sys/fs/cgroup/<path>/memory.reclaim
# 强制输出一次内存信息到 dmesg(需 sysrq 开启)
echo m | sudo tee /proc/sysrq-trigger
echo m > /proc/sysrq-trigger 只是 dump 内存状态,不是触发回收,也不会杀进程;线上操作前确认 kernel.sysrq 设置。
free 低不等于泄漏:page cache 和可回收 slab 会优先被回收,判断依据是 MemAvailable、直接回收和 PSI。allocstall/pgscan_direct 持续增长说明分配路径进入直接回收,表现为延迟抖动和 sy 升高,而不是单纯的“内存不够”。memory.events 和 dmesg。vm.swappiness 不是“是否使用 swap”的开关;它还影响文件页与匿名页的回收比例,且 cgroup v2 下行为受 memory.swap.max 影响。vm.min_free_kbytes 会减少可用内存并加重回收;调得过小则更容易触发直接回收和碎片化问题。memory.high 触发的是节流回收,可能造成明显延迟但不一定 OOM;memory.max 才更接近硬上限。pgscan 经验值不能直接套用。free 显示内存很少,为什么系统可能仍然健康?pgscan_direct 持续增长意味着什么?memory.high 和 memory.max 在回收行为上有什么区别?MemAvailable、直接回收计数和 PSI,而不是单看 free。memory.high 触发节流式回收,可能造成延迟但不一定杀进程;memory.max 是硬上限,回收失败后触发 cgroup OOM。