Linux cgroup v2 memory.events 中 high 与 oom 如何区分
来源:17golang原创
时间:2026-10-24 22:27:59 333浏览 收藏
Linux cgroup v2 中,memory.events 的 high 和 oom 不是同一个级别的告警:high 表示工作负载越过 memory.high 后被节流并进行直接回收,oom 表示已经触到内存限制,新的分配可能失败。前者重点看性能抖动,后者重点看分配失败和进程被杀。
官方文档:https://docs.kernel.org/admin-guide/cgroup-v2.html
快速判断:只有high增长,先查软上限和回收延迟;oom或oom_kill增长,则要按硬上限、分配失败和进程退出路径排查。不要把high计数直接当成 OOM。
先看字段:high、max、oom 和 oom_kill 各自说明什么
memory.events 是按键值记录的事件计数器,常见字段可以按严重程度和处置动作分开理解:
| 字段 | 含义 | 优先关注 |
|---|---|---|
high | 超过 memory.high,任务被节流并执行直接回收 | 延迟、吞吐和软上限是否过低 |
max | 使用量接近或越过 memory.max,内核尝试把它压回边界 | 硬限制、回收是否能释放空间 |
oom | 达到限制且分配即将失败,进入 OOM 状态的次数 | 失败分配和 OOM 处置路径 |
oom_kill | 该 cgroup 中被 OOM killer 杀掉的进程次数 | 退出进程、重启与工作负载影响 |
因此,high 更像“内存预算已经影响性能”的信号,oom 更像“继续分配已经有失败风险”的信号。它们可以先后出现,也可能只出现其中一种;不能只看一个字段就推断完整根因。

看限制:为什么 high 通常先影响性能,oom 才逼近失败
memory.high 是内存控制器的软边界。超过它后,cgroup 内的任务会被限制运行并被引导执行直接回收,目标是给管理程序留下调节内存或终止工作负载的机会。这个阶段可能出现请求变慢、GC 或缓存回收频繁,但不等于已经发生 OOM killer。
memory.max 是硬边界。内核会先尝试回收;如果无法把使用量降回限制以内,分配就可能进入 OOM 状态。oom 增长说明已经走到这个边界附近,oom_kill 是否增长则取决于最终是否真的有进程被 OOM killer 处置。
可以把两者看成两种不同的选型:想提前控制延迟,调优 memory.high;想限制工作负载最大占用,使用 memory.max,同时为 OOM 和恢复预留处理方案。提高 memory.high 不会改变 memory.max,设置 memory.max 也不会消除已经发生的 high 计数。
看归属:先区分层级统计,再判断当前用量
排查时不要只复制一行计数器。memory.events 的字段是层级统计,可能包含后代 cgroup 的事件;如果只想看当前 cgroup 自己产生的事件,应读取 memory.events.local。事件文件记录的是累计次数,不是当前内存用量,实时用量要结合 memory.current。
# 把目标 cgroup 改成实际的服务目录 CG=/sys/fs/cgroup/my-service # 读取层级事件:可能包含后代 cgroup 的贡献 cat "$CG/memory.events" # 只看当前层级事件,适合判断本组是否直接触发 cat "$CG/memory.events.local" # 读取当前用量和两道内存边界,单位通常为字节 printf 'current='; cat "$CG/memory.current" printf 'high='; cat "$CG/memory.high" printf 'max='; cat "$CG/memory.max" # 记录关键进程的 cgroup 归属,避免把别的服务计数算进来 cat /proc/"$PID"/cgroup
上面的命令只展示读取接口。若 memory.events 的 high 很高,而 memory.events.local 较低,优先检查后代服务是否在制造压力;若两者都高,再看当前 cgroup 自己的限制和进程归属。

按组合选动作:三种常见结果不要混为一谈
只有 high 增长:说明工作负载频繁越过 memory.high。先比较 memory.current、memory.high 与请求延迟,检查缓存、批处理或并发峰值是否让软边界过紧;如果业务可以承受更高峰值,再评估提高软边界或优化峰值分配。
oom 增长但 oom_kill 暂时不变:说明分配已经走到硬限制附近,但不代表一定已经杀进程。检查分配重试、调用方是否允许 OOM killer、限制值是否作用在预期层级,并结合服务日志判断请求是否失败。
oom 和 oom_kill 一起增长:按实际进程退出处理。记录被杀进程、父子进程关系、服务管理器的重启原因和 cgroup 层级;如果是多进程服务,还要确认主进程与 worker 是否属于同一个工作负载。不要只把 memory.max 调大而跳过泄漏、缓存和批量峰值的排查。
决策表:什么时候调 high,什么时候处理 max
| 现象 | 首要判断 | 建议动作 |
|---|---|---|
| high 持续增加,oom 为 0 | 软边界造成节流回收 | 查延迟、回收频率和工作集,必要时调整 memory.high |
| max、oom 增加,oom_kill 为 0 | 硬边界附近出现分配失败风险 | 查 memory.max、分配重试和请求失败日志 |
| oom_kill 增加 | 已有进程被 OOM killer 处置 | 保留 PID、归属、退出原因并检查恢复策略 |
| 层级 high 高、本地 high 低 | 压力主要来自后代 cgroup | 逐级读取 memory.events.local,定位实际产生者 |
最后记住三个边界:事件计数器是累计值,不是实时用量;层级文件可能包含后代贡献,不等同于本地事件;high 是节流与回收信号,oom 才表示分配已经逼近失败。按这三点读取,基本就能避免把性能问题误判成 OOM,或把真实 OOM 当成普通内存抖动。
相关问题
high 增长是不是说明内存泄漏?
不一定。它只能说明使用量越过 memory.high 并触发了节流回收,可能来自合法的工作集、缓存或瞬时并发峰值。要判断泄漏,还要观察 memory.current 的长期趋势和分配来源。
oom 增长后一定会看到 oom_kill 吗?
不一定。oom 记录的是达到限制且分配即将失败的事件,最终可能表现为分配失败、重试或 OOM killer 处置;只有真的杀掉进程时,oom_kill 才会随之增加。
-
426 收藏
-
387 收藏
-
242 收藏
-
238 收藏
-
402 收藏
-
162 收藏
-
128 收藏
-
273 收藏
-
175 收藏
-
372 收藏
-
418 收藏
-
486 收藏
-
167 收藏
-
448 收藏
-
128 收藏
-
117 收藏
-
125 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习