先看现象。一台 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 配额,而不是靠定时清缓存。报错文案容易误导,真正的原因往往在相邻那一层的日志里。