Linux cgroup v2用 memory.events 区分 oom 与 oom_kill的实现方法
排查容器或服务被 OOM 影响时,先不要把看到的 “oom” 直接等同于“进程已经被杀”。在 cgroup v2 中,memory.events 里的 oom 记录的是内存分配已经逼近失败的事件,而 oom_kill 记录的是 OOM killer 实际杀掉的进程数。两者要和 memory.max、memory.current 一起看,结论才不会偏。
oom增加,表示一次分配走到了 OOM 处理边界,不保证已经发生进程终止。oom_kill增加,才说明有进程被某种 OOM killer 杀掉。memory.events默认包含子 cgroup 的层级统计;只看当前组要读memory.events.local。
先把 cgroup 的内存边界读清楚
我更习惯先把观察对象固定成一个 cgroup 目录,再读四个文件。下面的路径只是示例,实际环境要替换成服务所在的 cgroup:
# 只读取目标 cgroup 的内存边界和事件文件,不扫描宿主机所有进程
CG=/sys/fs/cgroup/demo-worker
# memory.max 是硬上限,memory.current 是当前统计用量
printf 'max: '; cat "$CG/memory.max"
printf 'current: '; cat "$CG/memory.current"
printf '\n[events]\n'; cat "$CG/memory.events"
printf '\n[local events]\n'; cat "$CG/memory.events.local" 2>/dev/null || true
memory.max 返回字节数,也可能是 max,表示该组没有设置数值上限;memory.current 用来判断当前用量是否贴近上限。memory.events 是按键名组织的只读文件,不能假设字段永远按某个固定行号出现。

按字段读取 oom、oom_kill 和 max
生产脚本不要用 sed -n '4p' 这种按行号取值方式。用键名读取,既能容忍内核新增字段,也能把一次快照保存下来与下一次比较:
# 从 key value 文件中按字段名取计数;字段缺失时返回 0,便于兼容旧环境
event_value() {
local key="$1"
awk -v wanted="$key" '$1 == wanted { print $2; found=1; exit } END { if (!found) print 0 }' "$CG/memory.events"
}
# 这些是单调计数器,重点看本次采样相对上次的增量
OOM=$(event_value oom)
OOM_KILL=$(event_value oom_kill)
MAX_HIT=$(event_value max)
printf 'oom=%s oom_kill=%s max=%s\n' "$OOM" "$OOM_KILL" "$MAX_HIT"
内核文档对三个字段的含义不同:max 表示用量曾经要超过 memory.max;oom 表示已经到达限制且分配即将失败;oom_kill 表示属于该 cgroup 的进程被 OOM killer 终止。也就是说,oom 增加而 oom_kill 不变,并不矛盾,可能是分配以 -ENOMEM 返回、重试后成功,或者调用路径不适合触发 OOM killer。
oom 和 oom_kill 要按两个事实判断
可以把判断拆成“资源边界事实”和“进程结果事实”。第一组只说明内存分配遇到了压力,第二组才说明业务进程真的少了。不要用一条计数替代另一条:
| 观察结果 | 应作的判断 | 下一步 |
|---|---|---|
max 增加,oom 不变 | 触碰过硬上限,但尚未确认 OOM 分配失败 | 对照 memory.current 与业务峰值 |
oom 增加,oom_kill 不变 | 出现过 OOM 边界,可能由分配失败或重试结束 | 看服务错误处理和申请内存的调用方 |
oom_kill 增加 | 确实发生了进程终止 | 记录被杀进程、重启次数和内存上限变更 |
还有一个容易误读的点:memory.events 默认是层级统计,父 cgroup 的值可能包含子 cgroup 的事件。如果服务组里又划分了 worker 子组,父组的 oom 增加不一定发生在父组自己的进程上。要看当前组本地事件,就读取 memory.events.local。

把判断结果落成排障清单
一次排障至少保留三个时间点:服务开始升高前、计数第一次增加后、恢复或重启后。每次记录 memory.max、memory.current、max、oom、oom_kill 五个值,并注明读取的是层级文件还是 local 文件。
- 只有
memory.current长期接近上限:先检查缓存、批量任务和并发峰值,不要立刻归因于 OOM killer。 oom持续增加:检查申请失败后的错误处理,确认业务是否把-ENOMEM当成普通空结果。oom_kill增加:把进程退出时间与服务重启记录对齐,再决定是降低峰值、提高上限,还是拆分子 cgroup。
字段定义可继续查阅:https://docs.kernel.org/admin-guide/cgroup-v2.html。这个方法的边界也很明确:它解释 cgroup 内存事件计数,不代替宿主机全局内存、swap、内核日志和应用自身指标的联合排查。
相关问题
memory.events 和 memory.events.local 应该读哪个?
要看整个 cgroup 子树的累计影响,读 memory.events;只想判断当前组自己发生了什么,读 memory.events.local。监控父组时最好同时记录二者,避免把子组事件算到错误服务上。
oom 增加但 oom_kill 为零是不是误报?
不是误报。它表示分配曾经到达 OOM 边界,但结果可能是返回分配失败、被调用方忽略,或经过回收与重试后继续运行;只有 oom_kill 增加才确认有进程被杀。
为什么不能只看 memory.current?
memory.current 是当前值,无法告诉你刚才是否碰过上限,也无法区分分配失败和进程终止。事件计数提供了“发生过什么”的历史线索,两者要配合读取。
Go crypto/tls按 ServerName 选择证书的实现方式
- 上一篇
- Go crypto/tls按 ServerName 选择证书的实现方式
- 下一篇
- MCN第一次用LibTV怎么跑通一条内容?从选题卡、镜头表到审核回传
-
- 文章 · linux | 2小时前 | Linux · systemd · 故障排查 · 服务启动 · Linux systemd systemctl journalctl ExecStartPre
- Linux systemd识别 ExecStartPre 失败导致的未启动状态的实现方法
- 114浏览 收藏
-
- 文章 · linux | 3小时前 | Linux · systemd · 日志排查 · journalctl · 运维命令 · Linux journalctl journalctl按unit筛选日志 journalctl时间范围 systemd服务日志排查 journalctl日志为空
- Linux journalctl按 unit 与时间窗口筛选日志的实现方法
- 372浏览 收藏
-
- 文章 · linux | 6小时前 | linux运维 · systemd服务 · 故障重启 · 服务限流 · Linux systemd StartLimitBurst StartLimitIntervalSec Restart
- Linux systemd StartLimitBurst 如何限制失败重启频率
- 211浏览 收藏
-
- 文章 · linux | 8小时前 |
- Linux procfs fd 链接如何确认文件仍被进程持有
- 206浏览 收藏
-
- 文章 · linux | 9小时前 | Linux Rsync --checksum
- Linux rsync --checksum 何时值得付出额外扫描成本
- 381浏览 收藏
-
- 文章 · linux | 10小时前 | cron · 定时任务 · Linux · shell · crontab · Linux cron相对路径失败 cron找不到文件 crontab工作目录 cron环境变量 Linux定时任务排查
- Linux cron 使用相对路径失败时环境差异在哪里
- 360浏览 收藏
-
- 文章 · linux | 14小时前 | 命名空间 · 容器隔离 · 挂载 · Linux教程 · Linux mount namespace bind mount mount propagation
- Linux mount namespace 绑定挂载为何对宿主不可见
- 314浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 43次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 140次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 75次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 42次使用
-
- CMMLU
- 深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
- 27次使用
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览
-
- Linux系统下Go语言开发环境搭建
- 2023-01-07 242浏览
-
- 使用golang获取linux上文件的访问/创建/修改时间
- 2022-12-31 238浏览
-
- 在Linux系统中安装Go语言的详细教程
- 2022-12-29 402浏览

