先把当时的情况摆出来。Debian 12 上给打包任务套了 systemd-run –scope -p MemoryMax=1G,进程 RSS 只有六百兆,任务还是被判 OOM 杀掉,memory.events 里 oom_kill 每次加一,内核日志里却没有 oom-kill 那种大段输出。

排查方向其实有限,先把可能性排一排。cgroup v2 的 memory.current 把 page cache 和内核 slab 都算进去,RSS 只统计匿名页。还有一种是任务开头读了几百兆源文件,页缓存记在这个 cgroup 名下,一直没人回收。

另外,看的是 /proc 里的 RSS,它不含共享内存,也不含 tmpfs 占的页。少数情况下是子 cgroup 的用量要汇总到父级,父级先被撑到限额,账却算在子任务头上。

动手时先看这个 cgroup 的内存构成

grep -E '^(anon|file|slab|kernel) ' /sys/fs/cgroup/system.slice/*.scope/memory.stat

随后确认是 OOM 还是被限流卡住

cat /sys/fs/cgroup/system.slice/*.scope/memory.events

手工回收页缓存再跑一次

echo 800M > /sys/fs/cgroup/system.slice/*.scope/memory.reclaim

等前面都确认过,再确认没有同级 cgroup 叠加占用

systemd-cgtop -m -b -n 1 | head -10

恢复的判定其实很直接。memory.events 的 oom_kill 不再增长,memory.current 峰值稳定在八百兆上下,打包任务能完整跑完,memory.stat 里 file 一项回落到几十兆。

补一句。MemoryMax 卡的是整个 cgroup 而不是 RSS,估容量要按 memory.peak 取峰值再留两成余量;指望调大 max 会把宿主机一起拖下水,缓存想不计入只能靠 memory.high 提前回收。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189