OOM 不是“内存使用率高”这么简单。它表示内核或容器在满足内存分配请求时无法继续,只能选择进程终止。处理 OOM 的关键是区分宿主机 OOM、cgroup OOM、应用主动退出和 Kubernetes OOMKilled。
一、确认是否真的发生 OOM
journalctl -k --since '-2 hours' | grep -Ei 'out of memory|oom|killed process'
dmesg -T | grep -Ei 'out of memory|oom|killed process'记录发生时间、被杀进程、进程内存、cgroup、节点剩余内存和当时业务流量。Kubernetes 中继续查看:
kubectl describe pod <POD> -n <NS>
kubectl get pod <POD> -n <NS> -o jsonpath='{.status.containerStatuses[*].lastState}'退出码 137 只是收到 SIGKILL 的常见表现,需要结合事件确认是否 OOMKilled。
二、理解关键内存指标
free -h
cat /proc/meminfo
vmstat 1 10
ps -eo pid,user,%mem,rss,vsz,cmd --sort=-rss | head -30不要把 free 列很小直接理解为内存不足,Linux 会利用空闲内存做缓存,应重点看 available。RSS 表示进程实际驻留内存,VSZ 包含虚拟地址空间,不能单独用来判断泄漏。
三、确认谁在增长
pidstat -r 1 20
pmap -x <PID> | tail -20
cat /proc/<PID>/status
cat /proc/<PID>/smaps_rollup连续观察比单次快照更重要。若 RSS 随请求持续增长且业务低峰不回落,可能存在堆、直接内存、线程栈、本地缓存或内存映射泄漏。Java 应保留 heap dump 和 GC 日志;Go 可使用 pprof;Python 可结合 tracemalloc 分析。
四、检查 cgroup 和容器限制
cat /sys/fs/cgroup/memory.current 2>/dev/null
cat /sys/fs/cgroup/memory.max 2>/dev/null
cat /sys/fs/cgroup/memory.events 2>/dev/null宿主机还有大量内存,并不代表容器不会 OOM。容器达到自身上限时会触发 cgroup OOM。Kubernetes 的 request 用于调度,limit 才是上限;limit 设置过低会频繁 OOM,设置过高则可能造成节点超卖和抖动。
五、检查内核内存与缓存
slabtop
cat /proc/meminfo | grep -E 'Slab|SReclaimable|SUnreclaim|PageTables'大量连接、目录项、inode、网络缓冲区和内核对象也会消耗内存。不要在生产环境把 drop_caches 当作长期方案,它只能暂时释放可回收缓存,无法修复泄漏和容量不足。
六、紧急止血
摘除异常实例并保留证据;
临时扩容或降低并发;
回滚最近发布;
限制批任务和大查询;
在风险可控时重启泄漏进程;
避免同时重启整个集群造成雪崩。
七、长期治理
监控工作集、RSS、堆、直接内存、GC、cgroup 事件和 OOM 次数;
根据压测与历史峰值设置 request/limit;
为缓存设置数量和大小上限;
建立 heap dump、pprof 等现场采集机制;
进行容量评估,保留节点和系统组件安全余量。
总结
OOM 排查必须回答三个问题:谁被杀、为什么达到上限、增长来自哪里。只有把内核日志、进程内存、容器限制和业务变化串联起来,才能从“重启恢复”走向真正防复发。