快速修复
# 1. 确认是 OOM 被杀
docker ps -a --filter "status=exited" # Exited (137)
docker inspect <容器名> --format "{{.State.OOMKilled}} {{.State.ExitCode}}"
# true 137 = OOM;false 137 = 被人 kill -9 / 其它原因
# 2. 看内存实际用量
docker stats --no-stream # MEM USAGE / LIMIT 两列
# 3. 内核侧证据(可选)
dmesg | grep -i "oom-kill" | tail
# compose 里设限制 + 崩溃兜底重启
services:
app:
image: your-image
mem_limit: 512m # 或 deploy.resources.limits.memory: 512M
restart: unless-stopped # OOM 被杀后自动拉起(但会反复重启 = 还是要治本)
你会遇到什么
容器里的进程跑着跑着突然没了:docker ps -a 显示 Exited (137),docker inspect 里 State.OOMKilled=true;服务「无缘无故」重启、消失,日志里却什么都没有(进程是被内核直接 SIGKILL 的,来不及写日志)。
容器内存超过了它的 cgroup 限制,被内核 OOM killer 杀掉——不是整机内存不足,是容器自己的配额用完了。
本文路径:快速修复(三条排查命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。
为什么会这样
docker run -m 64m(compose 的 mem_limit / deploy.resources.limits.memory)给容器设 cgroup 内存上限;
进程累计内存(含 page cache 归属)超过上限时,内核 OOM killer 在该 cgroup 内选进程杀掉——被杀的容器 exit code = 137(128 + SIGKILL 9)。不设 -m 的容器没有内存上限,可以吃满整台宿主机,把别的服务一起拖死。
Docker 用 cgroup v2 的 memory.max 实现限制(实测:-m 128m 的容器内 cat /sys/fs/cgroup/memory.max = 134217728)。
OOM 触发时内核记 oom-kill:constraint=CONSTRAINT_MEMCG,...task=python3(实测 dmesg 原文),这是「cgroup 级 OOM」的确定性证据——和整机内存不足(constraint=CONSTRAINT_NONE)是两类。
两个默认值要记住:--memory-swap 默认 = --memory × 2(即容器最多可用 2 倍内存的 swap,实测 help 文档语义),-1 表示 swap 不限;docker stats 的 MEM USAGE / LIMIT 是观察入口。
排查链是确定的:docker ps -a 看 137 → docker inspect <容器> --format "{{.State.OOMKilled}}" 确认 → docker stats 看实际用量 → dmesg | grep oom 看内核记录。
确定性推论:exit 137 不一定是 OOM(也可能是手动 kill),先看 OOMKilled 字段再下结论;Java/Python 等有自己内存池的应用,容器限制要按「进程 RSS + 运行时开销」算,不是按应用堆配置算。
一步步来
1. 确认 OOMKilled — 必做
docker inspect <容器名> --format "{{.State.OOMKilled}} {{.State.ExitCode}}"
true 137 → cgroup OOM(本坑);false 137 → 外部 kill -9 或平台回收,另查。
2. 量化内存用量 — 必做
docker stats --no-stream <容器名>
# MEM USAGE / LIMIT:用量接近 LIMIT 就是配额不够
3. 设/调限制 — 按诊断
- 没设
-m:先设一个合理上限(-m 512m/ composemem_limit),防止单容器吃满宿主; - 设了还 OOM:把限制调大,或治本(见第 4 步);
- swap:默认
--memory-swap=--memory× 2;--memory-swap -1表示 swap 不限(谨慎,可能把宿主 swap 打爆)。
4. 治本:查进程内存模型 — 按诊断
Java:-Xmx 只算堆,容器限制要再加元空间/线程栈/直接内存(常见方案:-XX:MaxRAMPercentage=75);Python:大对象/缓存/bytearray 等一次性分配(实测 256MB 分配撞 64m 限制即被杀);Node:--max-old-space-size。改完代码/参数后重建容器。
5. 验证 — 必做
docker run --rm -m 128m your-image your-app # 稳定运行
docker stats --no-stream # 用量明显低于 LIMIT
docker inspect <容器名> --format "{{.State.OOMKilled}}" # false
怎么确认修好了
docker ps 里容器稳定 Up(不再反复 Exited 137);docker inspect 的 OOMKilled=false;docker stats 显示用量低于限制且有富余(实测:128m 限制、96MB 分配时 stats 显示 100.1MiB / 128MiB 78.22%)。
其它环境的表现
| 场景 | 表现 |
|---|---|
无 -m 限制 | 容器可吃满宿主内存,拖垮其它服务(默认无上限) |
-m 64m + 分配 256MB | 进程被杀:exit 137、OOMKilled=true、dmesg 记 constraint=CONSTRAINT_MEMCG(实测) |
--memory-swap | 默认 = memory × 2;-1 = swap 不限;调大可在内存峰值期缓冲 |
| exit 137 但 OOMKilled=false | 外部 kill -9(如平台回收),不是本坑 |
症状
$ docker ps -a
oomdemo2 ... Exited (137) 4 seconds ago
$ docker inspect oomdemo2 --format "{{.State.OOMKilled}} {{.State.ExitCode}}"
true 137
# 内核侧(dmesg 原文节选)
oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=docker-...scope,task=python3,pid=24719,uid=0
Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB
特征:容器 exit 137 + OOMKilled=true;应用日志里没有报错(SIGKILL 不经过应用);docker stats 用量贴着 LIMIT。
原因
容器进程累计内存超过 cgroup(memory.max)限制,内核 OOM killer 在 cgroup 内杀进程,exit code = 137(SIGKILL)。不设 -m 则无上限。默认 --memory-swap = 2×--memory。
解法
docker inspect确认 OOMKilled=true;docker stats量化用量,dmesg | grep oom-kill取内核证据;- 设/调
-m(composemem_limit),必要时调--memory-swap; - 治本:按运行时模型配(Java MaxRAMPercentage / Node max-old-space-size / Python 大分配)并重建容器;
- 验证 OOMKilled=false、stats 有富余。
验证证据
- 本机实测复现(2026-08-31,WSL 定制预构建版,docker 29.1.3):
- 基线:无限制容器分配 256MB 成功(
alloc ok 268435456); -m 64m+ 分配 256MB → 进程被杀,docker run退出码 137;docker inspect→OOMKilled=true ExitCode=137;- dmesg 原文:
oom-kill:constraint=CONSTRAINT_MEMCG,...,task=python3,pid=24719+Memory cgroup out of memory: Killed process 24719 (python3) total-vm:273380kB, anon-rss:64640kB; -m 128m容器内cat /sys/fs/cgroup/memory.max= 134217728(128MiB);docker stats显示100.1MiB / 128MiB 78.22%;--memory-swap默认=2×memory 语义按 docker help 文档核对。- 实测复现日志(2026-08-31)
相关 canonical
- docker system prune 清理边界(同批 Docker 域资源问题)
- 容器时区/中文乱码(同批容器运行配置坑)
适用工具 (1)
本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。
docker · 外部工具来源与证据
← 编码代理与终端 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡