Linux 遇到 OOM(内存不足)怎么办?新手必看的解决指南
不管你是刚入门还是已经在服务器上摸爬滚打了一段时间,“进程突然消失”这种问题应该都遇到过:上午还跑得好好的 Java 服务,下午 SSH 上去发现进程没了,日志里面什么都没留下;或者数据库在高峰期突然连接不上,重启之后又能坚持几天。去查系统日志,看到一行 Out of memory: Killed process——这就是 OOM(Out Of Memory),换成人话说就是:内存不够用了,内核启动了一个“大扫除”,把你某个进程直接杀掉来腾内存。
这篇文章不打算讲那种让人打瞌睡的理论,而是把我在生产环境里排查 OOM 的经验全部倒出来。从“内核为什么杀我的进程”到“怎么快速定位谁吃了内存”,再到“怎么配置才能尽量不触发 OOM”,全程配指令、配参数、配上坑记录。不管你是刚接触 Linux 的新手,还是已经被 OOM 折磨了几次的运维同学,这篇文章应该都能帮你少走弯路。
1. OOM 是怎么发生的:本质是“内存超卖”的代价与必然
1.1 先理解 Linux 内存分配的逻辑:为什么它敢“过量分配”
很多人对 OOM 的第一个误解是:是不是内存真的被占满了才会触发?答案是:不一定。
Linux 内核默认采用了 Overcommit(超额分配) 策略。什么意思呢?就是一个程序向内核申请内存时,内核不会真的去检查“现在物理内存还有多少空闲”,而是先答应下来,等程序真正写入数据的时候才实际分配物理页。这就像信用卡:银行给你的额度可能比你实际存款高很多,只要你不刷爆,大家相安无事。
这种做法带来一个极大的好处:现代程序(尤其是 fork 出来的子进程、Redis 的 copy-on-write)在申请大块内存但实际使用率不高的时候,系统不至于因为保守而浪费大量物理内存。代价则是:当所有程序同时开始“玩命写内存”,物理内存瞬间被耗尽,内核就得立刻找一个“背锅侠”杀掉,好把空间腾出来。这个背锅侠的挑选过程,就是 OOM Killer(内存耗尽杀手) 的职责。
1.2 内核是如何选出“背锅侠”的
很多新手以为内核会随机挑一个进程杀掉,其实不是。每个进程都有一个 oom_score(分数),分数越高的越容易被杀。这个分数由两部分决定:
- 进程实际使用的内存大小(RSS):用得越多,分数越高。
oom_score_adj(调减值):这是系统管理员可以手动干预的数值,范围从 -1000 到 1000。比如设置成-1000,基本就告诉内核“别杀我”,通常用于关键系统进程。
另外在内核编译以及启动参数中,还有 vm.panic_on_oom、vm.overcommit_memory 这些开关。默认情况下 vm.panic_on_oom=0,表示触发 OOM 时只杀进程、不重启系统;如果设置成 1 或者 2,可能会直接触发 kernel panic 导致整机重启。
这里我想强调一个核心观点:OOM Killer 不是要“惩罚”谁,而是要在最短的时间内释放最多的内存来保住系统。所以它一定优先选那些内存占用大、又看起来“不那么重要”的进程。很遗憾,Java 应用、数据库、大数据计算任务往往就是这样的目标。
1.3 什么时候该慌,什么时候是“良性 OOM”
不是每次出现 OOM 日志都意味着你的服务代码有问题。我总结为三种常见类型:
- 一次性峰值导致突发 OOM:比如双 11 秒杀、活动抽奖,流量瞬间上来,内存短暂见底。这类 OOM 如果发生在可容忍的抖动窗口内,通过扩容或者加缓存就能解决。
- 内存泄漏型 OOM:进程 RSS 持续上升,GC 之后回不来,折腾几天甚至几周后终于被 kill。这种最危险,必须从代码层面排查。
- 系统内存被缓存吃干导致 OOM:Linux 会把空闲内存用作 page cache(文件缓存),这是好事情,但如果你完全没有配置 swap,且缓存不可回收,也会触发 OOM。这种情况偶尔会发生在某些特殊的内核配置或者容器环境中。
拿到一例 OOM 日志,先别急着“调 JVM 参数”或者“加内存条”,你要先回答三个问题:是哪个进程被杀了?它大致吃了多少内存?当时系统总内存和剩余内存是多少?答案基本都在内核日志里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一现场:快速定位“到底是谁 OOM 了”
2.1 从内核日志里捞出关键证据
现在假设你的进程真的被 OOM Kill 了,第一件事不是启动 IDE 改代码,而是去日志里找证据。在绝大多数 Linux 发行版上,内核日志
