news 2026/9/30 2:40:30

Docker 容器 OOM 被杀?内存限制与排查完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker 容器 OOM 被杀?内存限制与排查完整指南

容器跑着跑着突然消失,docker ps 里找不到了,应用日志最后一行还停在正常的业务输出上,没有任何报错——这是 OOM(Out Of Memory)被杀的典型表现。它和普通崩溃不一样:进程是被内核直接 SIGKILL 的,来不及写日志、来不及清理,所以现场特别干净,干净到让人摸不着头脑。

这篇讲清楚三件事:怎么确认容器是被 OOM 杀的、为什么会 OOM(宿主 OOM 和 cgroup OOM 是两回事)、以及怎么用内存限制参数和 JVM 配置从根上防住。

第一步:确认是不是 OOM

看容器退出状态:

docker ps -a --filter "status=exited" # 或针对具体容器 docker inspect <容器名> --format '{{.State.Status}} {{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}'

输出:

exited 137 OOMKilled=true

两个关键信号:

  • ExitCode 137= 128 + 9,即收到 SIGKILL
  • OOMKilled=true= Docker 明确标记这个容器是因内存超限被杀

如果 OOMKilled=true,基本可以定性为cgroup OOM(容器自己的内存限制被触发)。如果 OOMKilled=false 但 ExitCode 137,可能是被人 docker kill 了,或者宿主机级别 OOM(见下)。

看内核日志确认宿主机 OOM:

dmesg -T | grep -i "oom\|killed process" | tail

输出类似:

[Wed Sep 24 14:32:11 2026] Memory cgroup out of memory: Killed process 12345 (java) total-vm:4194304kB ... [Wed Sep 24 14:32:11 2026] Out of memory: Killed process 6789 (java) ...
  • 出现 Memory cgroup out of memory → cgroup OOM(容器限制触发)
  • 出现 Out of memory: Killed process(没有 cgroup 字样)→宿主机全局 OOM,内核在整机内存不足时挑进程杀,可能杀到没设限制的容器

这两者的处理思路完全不同,务必先分清。

两种 OOM 的根因

cgroup OOM(容器限制触发):

容器设置了 --memory(或 Compose 的 mem_limit),当容器内存用量超过限制,内核的 cgroup OOM killer 杀掉容器内进程。常见原因:

  • 限制设得太低,应用正常负载就需要更多
  • 应用内存泄漏,缓慢增长直到撞线
  • JVM 堆外内存(Metaspace、DirectByteBuffer、线程栈)超出预期,堆没满但总内存超了

宿主机 OOM(整机内存不足):

容器没设限制(或限制总和超过物理内存),多个容器一起把宿主机内存吃满,内核全局 OOM killer 出手,按 oom_score 挑进程杀——往往杀掉内存占用最大的那个,可能根本不是"肇事者"。

# 看宿主机内存现状 free -h # 看各容器实时内存 docker stats --no-stream

docker stats 输出:

CONTAINER ID NAME MEM USAGE / LIMIT MEM % a1b2c3d4e5f6 app 1.8GiB / 2GiB 90.00% d4e5f6a7b8c9 mysql 900MiB / 4GiB 21.48%

MEM USAGE / LIMIT 里 LIMIT 显示的是该容器的内存上限;没设限制时显示宿主机总内存。MEM % 持续在 90% 以上的容器就是高危对象。

内存限制参数详解

--memory(-m):硬上限。容器内存用量超过即触发 cgroup OOM。

docker run -d --memory 2g --name app myapp:1.0

--memory-swap:内存 + swap 的总上限。默认等于 --memory 的 2 倍(即允许等量 swap)。设为与 --memory 相同则禁用 swap;设为 -1 表示 swap 不限。

# 内存 2g,完全禁用 swap docker run -d --memory 2g --memory-swap 2g app # 内存 2g,额外允许 1g swap(总 3g) docker run -d --memory 2g --memory-swap 3g app

生产环境建议 --memory-swap 等于 --memory(禁 swap),避免容器用 swap 导致性能抖动且掩盖真实内存压力。

--memory-reservation:软限制。内存紧张时才生效,用于调度参考,不强制。

--oom-score-adj:调整容器被宿主机全局 OOM killer 选中的优先级,范围 -1000~1000。值越高越容易被杀。关键容器可以调低:

docker run -d --oom-score-adj -500 --name critical-db mysql:8.4

--oom-kill-disable:禁止杀该容器(仅 cgroup OOM)。慎用——容器超限时不杀进程会导致内存一直涨,最终拖垮宿主机。一般只在搭配 --memory 且你明确知道后果时用。

对已运行的容器改限制(不用重建):

docker update --memory 4g --memory-swap 4g <容器名>

Compose 里的写法(v2):

services: app: image: myapp:1.0 deploy: resources: limits: memory: 2g

注意 deploy.resources.limits 在 docker compose up(非 swarm)下也生效。老写法 mem_limit: 2g 仍可用但推荐 deploy 块。

Java 应用的特殊坑:JVM 不认容器限制

这是 Java 容器 OOM 的头号原因。JDK 8 早期版本(8u131 之前)不感知 cgroup 内存限制,JVM 按宿主机物理内存计算默认堆大小。比如宿主机 32G,JVM 默认堆可能开到 8G,而容器限制只有 2G——堆还没到 8G,容器总内存先撞 2G 上限,cgroup OOM 杀掉进程。

解法一:升级 JDK 并启用容器感知。JDK 8u191+ / 10+ 默认开启 UseContainerSupport,JVM 会读 cgroup 限制。确认:

java -XX:+PrintFlagsFinal -version 2>&1 | grep UseContainerSupport # 期望:bool UseContainerSupport = true

解法二:用 MaxRAMPercentage 代替固定 -Xmx。让堆按容器内存限制的百分比分配,而不是写死:

java -XX:MaxRAMPercentage=75.0 -jar app.jar

容器限制 2G 时,堆上限约 1.5G,剩下 0.5G 留给 Metaspace、线程栈、DirectBuffer 等堆外内存。不要设 100%,堆外内存没地方放照样 OOM。75% 是常用值。

解法三:显式 -Xmx 但要算上堆外。如果坚持写死:

# 容器限制 2g,堆设 1.2g,留 0.8g 给堆外 java -Xmx1280m -jar app.jar

排查堆外内存:如果堆没满但容器 OOM,用 Native Memory Tracking 看分布:

# 启动时加 java -XX:NativeMemoryTracking=summary -jar app.jar # 运行中查看 jcmd <pid> VM.native_memory summary

输出会列出 Heap、Class(Metaspace)、Thread、Code、GC、Internal 等各区域占用,定位是 Metaspace 涨(类加载泄漏)还是 Thread 涨(线程创建过多)还是 Internal 涨(DirectByteBuffer)。

完整排查流程

一次真实的 OOM 排查过程:

# 1. 确认死因 docker inspect app --format '{{.State.ExitCode}} {{.State.OOMKilled}}' # → 137 true ⇒ cgroup OOM # 2. 看限制是多少 docker inspect app --format '{{.HostConfig.Memory}}' # → 2147483648 (2g) # 3. 看内核日志佐证 dmesg -T | grep -i "memory cgroup" | tail -3 # 4. 重启容器后监控增长曲线 docker stats app # 观察 MEM USAGE 是否持续单调上涨(泄漏特征)还是平稳(限制过低特征) # 5. 进容器看 JVM 内存分布(Java 应用) docker exec -it app jcmd 1 VM.native_memory summary docker exec -it app jmap -heap 1 # 6. 定性 # - 平稳但接近上限 ⇒ 限制过低,调大 --memory 或调小 -Xmx/MaxRAMPercentage # - 单调上涨不回落 ⇒ 内存泄漏,dump 堆分析 docker exec app jmap -dump:live,format=b,file=/tmp/heap.hprof 1 docker cp app:/tmp/heap.hprof ./heap.hprof # 拷出来用 MAT/JVisualVM 分析

泄漏 vs 限制过低的判别:docker stats 里内存曲线平稳波动 = 限制过低或堆外配置不当;持续单调上涨不回落 = 泄漏。这个判别能省掉一大半排查时间。

防住 OOM 的四条配置

  1. 每个容器都设 --memory:不设限制的容器在宿主机 OOM 时是随机牺牲品,设了限制至少死得可控、可预期
  2. --memory-swap 等于 --memory:禁 swap,避免性能抖动和掩盖内存压力
  3. Java 用 -XX:MaxRAMPercentage=75:让堆自适应容器限制,留出堆外空间
  4. 监控告警:对 docker stats 的 MEM % 或 cgroup 的 memory.current 设 80% 告警,在 OOM 之前发现趋势

Prometheus 场景可以抓 cgroup 指标:

# cgroup v2 路径 cat /sys/fs/cgroup/system.slice/docker-<容器id>.scope/memory.current cat /sys/fs/cgroup/system.slice/docker-<容器id>.scope/memory.max

memory.current / memory.max 持续 > 0.8 就该扩容或排查了。

小结

容器 OOM 分两种:cgroup OOM(OOMKilled=true,容器限制触发)和宿主机 OOM(内核全局 killer,可能误杀)。确认用 docker inspect 的 ExitCode 137 + OOMKilled 字段和 dmesg。

防住靠三点:给每个容器设 --memory 且 --memory-swap 同值禁 swap;Java 应用用 MaxRAMPercentage 让堆感知容器限制并留堆外空间;用 docker stats 曲线区分"限制过低"和"内存泄漏",前者调参后者 dump 堆。

记住:OOM 现场干净不是没原因,是进程被 SIGKILL 来不及说话。学会从 OOMKilled 字段和 dmesg 里读出现场,这类问题就不再是玄学。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 2:40:28

大厂校招薪资揭秘:前端、AI、测开方向薪资大盘点,小白必看!

本文汇总了字节跳动、腾讯、阿里巴巴、京东、美团、百度、快手、网易、小米、滴滴等热门大厂校招薪资情况&#xff0c;涵盖前后端、AI、测开等方向。数据基于往届案例&#xff0c;包括公开信息和内部渠道。文章详细对比了各厂的薪资待遇、签字费、股票等福利&#xff0c;并提醒…

作者头像 李华
网站建设 2026/9/30 2:39:52

自动捡球机器人4—调研4之采购

文章目录一、你今天第一件事&#xff1a;什么都别买&#xff0c;先量羽毛球二、第一台机器长什么样三、第一批采购&#xff1a;控制在 500&#xff5e;800 元A. 工具——以后整个机器人都要用B. 电源&#xff1a;你千万先不要买裸露的 220V 开关电源四、第一台实验机只需要一个…

作者头像 李华
网站建设 2026/9/30 2:39:21

中药煎药质量参差不齐?全链路自动化工控系统完整实战

做医药工控的朋友应该都知道,中药煎药自动化是看着简单做着难的典型。表面看就是把饮片放锅里加水煮,实际上牵扯到处方解析、多锅调度、先煎后下、火候控制、药液过滤、包装赋码、全程追溯,每个环节都要精准可控,还要符合GMP规范。很多项目上线后才发现:火候控制不准,每锅…

作者头像 李华
网站建设 2026/9/30 2:39:08

机油认证标号 API SP、ACEA,到底怎么看?

很多工程师和爱动手的车主看机油桶&#xff0c;只认粘度和品牌&#xff0c;对"API SP"“ACEA A3/B4"这类字母数字组合基本无视。直到认真选油才发现&#xff0c;这些标号可能比品牌更重要——它们是机油够不够格的"准考证”。作为「美洲豹功能润滑油」的技…

作者头像 李华
网站建设 2026/9/30 2:38:37

WhatsApp 客户管理工具:从记事本到 CRM 的过渡

我见过太多团队&#xff0c;花了钱上了一套 CRM&#xff0c;三个月后业务员又回去用微信收藏夹记客户。 问题不在 CRM 不好&#xff0c;在跳得太快。今天讲讲这条路该怎么分三段走。一、为什么一次性上 CRM 大多失败 先讲个我亲眼见过的事。 一个十来个人的外贸团队&#xff0c…

作者头像 李华
网站建设 2026/9/30 2:38:13

C语言:(15.文件操作)

一、为什么需要文件&#xff1f;程序运行时的数据都在内存中&#xff0c;程序结束就消失了。文件可以让数据持久化保存。// 内存中的结构体变量 struct student {char name[20];int age;double score;long long ID; };// 文件 保存在磁盘上的结构体数据 // 可以保存文字、视频…

作者头像 李华