先看现象。一台 8 核 64G 的 CentOS 7 跑 Nginx 和 PHP,凌晨 OOM killer 杀掉 php-fpm,事后 free -g 显示 available 还有二十多 G,cache 也不大。

排查方向其实有限,先把可能性排一排。dentry 和 inode 的 slab 缓存占了大头,free 里算作可回收但内核没能及时收缩。少数情况下是容器和大量小文件场景让 dentry 数量暴涨,shrinker 回收跟不上分配速度。

不容易想到的是,内存碎片化严重,连续大页分配失败,触发 OOM 但剩余内存并不少。

还有一种情况,cgroup 的内存上限比物理内存小,进程被限制在某个子组里先撞线。

第一件事是看 slab 里到底是哪一类在涨

slabtop -o -s c | head -15

接着按 cgroup 看各组的实际用量

systemd-cgls --no-pager | head -20 && cat /sys/fs/cgroup/memory/memory.usage_in_bytes

手动回收一次 slab 观察是否回落

echo 2 > /proc/sys/vm/drop_caches && free -m

调低缓存回收的激进度

sysctl -w vm.vfs_cache_pressure=200

恢复的判定其实很直接。slabtop 里 dentry 与 inode 的活跃对象数明显下降,available 与实际可分配内存一致,随后一周内没有再出现 OOM 记录。

补一句。drop_caches 只能验证不能常态化,线上跑会让缓存冷启动;真正的解法是控制文件数量与 cgroup 配额,而不是靠定时清缓存。报错文案容易误导,真正的原因往往在相邻那一层的日志里。

作者 9823189