oom是内核在物理内存与swap均耗尽、缓存已回收时,为保系统而杀进程的最后一道防线;需通过available内存和swap使用率判断真缺内存,结合dmesg日志定位肇事进程,再针对性调优配置。

OOM 不是“程序写错了就该报错”,而是内核在物理内存彻底见底、缓存已回收、swap 已打满时,被迫杀进程保系统的最后一道防线。直接调大 JVM 堆或加 swap 文件,治标不治本;真正要做的,是分清“真缺内存”和“假缺内存”,再针对性干预。
怎么确认是真 OOM 还是缓存占满导致的误判
Linux 的 free -h 输出里 available 才是真正可用内存,buff/cache 高 ≠ 内存不足——那是内核在用空闲内存做文件缓存,随时可回收。只有当 available 接近 0 且 swapon --show 显示 swap 使用率 >95%,才说明物理内存 + swap 全被压垮。
常见误判现象:
-
free -h显示used98%,但available还有 2GB → 实际没压力,不用动 -
top里某个进程%MEM占 40%,但它是 Redis 或 MySQL 的 buffer,属于合理占用 -
dmesg | grep -i "out of memory"没输出,但journalctl -k | grep -i "kill process"有记录 → 真 OOM 已触发
查日志 + 定位肇事进程必须一步到位
OOM Killer 日志里那行 Out of memory: Kill process 1234 (java) 是唯一可靠线索,PID 和进程名不能靠猜。
实操建议:
- 先用
dmesg -T | grep -i "out of memory"(带时间戳)找最近一次触发点 - 用
ps -p 1234 -o pid,comm,args确认进程真实用途,别只看comm是java就删 —— 可能是你的核心服务 - 结合
/proc/1234/status查VmRSS(实际物理内存占用),比top的估算更准 - 如果同一进程反复被杀,大概率是内存泄漏,不是配置问题
调整 vm.swappiness 和 vm.min_free_kbytes 很容易翻车
vm.swappiness=0 并不等于“禁用 swap”,只是降低倾向性;而 vm.min_free_kbytes 设太高(比如设成 2GB),会强制系统预留大量内存不给应用用,反而加剧 OOM。
安全操作边界:
- 对 32GB 内存服务器,
vm.swappiness建议设为10~30(MySQL/Redis 场景推荐10) -
vm.min_free_kbytes应设为总内存的 1%~2%,32GB 机器建议327680(320MB),不要盲目抄 “1GB” 配置 - 改完必须运行
sysctl -p生效,且要验证:cat /proc/sys/vm/swappiness
临时缓解后,必须检查三类配置硬伤
很多 OOM 是因为配置值和物理内存严重不匹配,而不是负载高。
重点核对:
- MySQL 的
innodb_buffer_pool_size:32GB 机器上设 20GB 是危险的,建议 ≤16GB,并确保free -h中available始终 >2GB - Java 应用的
-Xmx:若设-Xmx16g,系统需预留至少 2GB 给 OS + 其他进程,否则一启动就逼近红线 - Docker 容器未设
--memory限制:单个容器吃光所有内存,其他服务直接陪葬
最常被忽略的一点:oom_score_adj 不是万能开关。把关键服务设为 -1000 只会让 OOM Killer 转头杀掉下一个最大进程,可能恰好是数据库连接池或 SSHD,导致运维失联。真要保关键服务,得配合 cgroups 限流 + 应用层降级,而不是靠隐藏分数。











