Linux cgroups v2 内存 怎样处理
来源:17golang原创
时间:2026-10-22 10:10:42 126浏览 收藏
Linux cgroups v2 内存问题,先不要把所有现象都叫作“内存不够”。正确的处理顺序是:先读 memory.current 建立当前用量,再用 memory.high 施加可恢复的回收压力,最后用 memory.max 作为硬边界;出现变慢、回收或进程被杀时,再结合 memory.events 和 memory.stat 判断原因。
memory.high超限通常意味着回收和限流,不等于 OOM。memory.max是硬上限,无法回收到边界内时才会在该 cgroup 内触发 OOM。memory.events看事件,memory.stat看构成,两者要和当前用量一起读。

一、先区分当前用量和限制动作
排查一个 cgroup 的内存问题,第一组指标应该固定下来:memory.current 表示该 cgroup 及其后代当前使用的内存总量;memory.high 是超过后施加重回收压力的阈值;memory.max 是无法回收到边界内时触发 cgroup 内 OOM 处理的硬上限。三者的职责不同,不能只看一个数字下结论。
下面的例子假设工作负载位于 /sys/fs/cgroup/demo-worker。命令只展示读取和配置路径,实际操作前应确认该目录属于统一层级且当前进程具有相应权限。
# 先记录当前用量和两个边界,避免只凭进程 RSS 猜测 cg=/sys/fs/cgroup/demo-worker printf 'current='; cat "$cg/memory.current" printf 'high='; cat "$cg/memory.high" printf 'max='; cat "$cg/memory.max" # 记录事件快照,后续调整后用同样的字段做前后对比 cat "$cg/memory.events" cat "$cg/memory.stat" | sed -n '1,20p'
基线至少保存两次:一次在业务平稳时,一次在症状出现时。若 current 接近 high 但 max 还很远,重点应放在回收压力和吞吐下降;若已经反复触碰 max,才进入硬上限排查。
二、用 memory.high 处理可恢复的压力
memory.high 更适合作为第一道治理边界。工作负载超过它时,内核会让该 cgroup 承受直接回收和限流压力,但这个边界本身不会调用 OOM killer。它的价值是给监控和管理程序留出时间:降低并发、释放缓存、迁移任务或回滚配置。
例如,先把测试工作负载的高水位设为 768 MiB,再观察事件和延迟变化:
# 设定可恢复的高水位;写入前确认这是测试或已批准的 cgroup echo 768M > "$cg/memory.high" # high 超限通常会增加 high 计数,用它和业务延迟一起判断是否过紧 grep -E '^(high|max|oom|oom_kill) ' "$cg/memory.events" # 若需要撤销高水位,max 表示不设置这个边界 echo max > "$cg/memory.high"
如果 high 持续增长、请求延迟上升但 oom_kill 没有变化,典型解释是高水位过低或工作负载本来就需要更大的活跃集。此时应结合业务延迟、吞吐和回收行为逐步上调,而不是直接把 high 与 max 设成同一个值。
三、用 memory.max 设置最后一道边界
memory.max 解决的是“这个工作负载最多允许占用多少内存”。当使用量达到它且无法继续回收时,OOM 处理会被限制在对应 cgroup 的资源域内;在某些分配和并发时刻,观察到的用量也可能短暂超过该值。因此它是安全兜底,不是精确的实时仪表盘。
# 先保留一个比 high 更高的硬边界,避免回收压力立即演变成 OOM echo 1G > "$cg/memory.max" # 调整后同时观察当前用量和 OOM 相关事件 cat "$cg/memory.current" grep -E '^(max|oom|oom_kill|oom_group_kill) ' "$cg/memory.events" # 发生误配时可恢复为无限制,但这不是替代容量规划的长期方案 echo max > "$cg/memory.max"
下调 memory.max 时不要把“写入成功”当作“工作负载已经稳定”。内核还需要回收,无法满足边界时可能发生 OOM;生产环境应先通过 memory.current、事件计数和应用级错误率确认回滚窗口,再改变硬限制。
四、通过 memory.events 和 memory.stat 定位原因
读事件时,先看 high、max、oom 和 oom_kill。其中 high 增长说明曾经超过高水位并受到压力;max 增长说明触及硬边界;oom 说明发生了 OOM 处理尝试;oom_kill 则表示实际杀死进程的次数。若使用了 swap,还应注意相关 swap 事件字段是否增长。
memory.events 是层级统计,后代 cgroup 的事件也可能反映在上层;需要只看当前 cgroup 的本地事件时,可读取 memory.events.local。这能避免把子服务的压力误归因给父目录。

# 先看层级事件,再看本地事件,区分父 cgroup 与当前目录的影响
cat "$cg/memory.events"
cat "$cg/memory.events.local"
# 按键读取构成,不依赖字段在文件中的固定顺序
awk '$1 == "anon" || $1 == "file" || $1 == "shmem" || $1 == "slab" {print}' "$cg/memory.stat"
如果 anon 占比高,通常要回到应用堆、线程栈或匿名映射寻找增长点;如果 file 占比高,则要判断文件缓存是否符合工作负载预期。memory.stat 的键可能随内核版本增加,脚本应按键查找,不要依赖固定行号。
五、把配置落成可回滚的排障步骤
- 先保存平稳期和异常期的
memory.current、memory.events、memory.stat。 - 只调整
memory.high,观察回收压力、延迟和high计数,确认是否是软边界过紧。 - 确认工作负载的合理峰值后,再把
memory.max放在高水位之上作为最终保护。 - 出现 OOM 时,用
oom_kill和本地事件确认杀进程发生在哪个 cgroup,再回到anon、file等构成字段。 - 每次只改一个边界,并保留可执行的回滚命令;不要把临时解除限制当成长期修复。
最常见的误区是把 memory.high 当成“超过就杀进程”,或把 memory.current 当成“应用真正需要的内存”。前者会导致告警和动作设计错误,后者会让缓存型工作负载被过度扩容。把边界、事件和构成放在同一张指标面板上,才能判断是配置问题还是应用增长问题。
相关问题
memory.high 超过后一定会 OOM 吗?
不会。它主要触发回收和限流,应该先关注延迟、吞吐和 high 事件。
为什么 memory.current 可能短暂超过 memory.max?
内核文档允许某些情况下出现临时超额,不能把一次读数直接当成稳定状态。
memory.events 和 memory.events.local 选哪个?
要看当前目录及后代的累计影响时看前者;只定位当前 cgroup 本地事件时看后者。
memory.stat 为什么不能按第几行解析?
字段是按键组织的,内核可能在中间增加新键,脚本应按字段名匹配。
资料依据:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
-
160 收藏
-
105 收藏
-
426 收藏
-
387 收藏
-
242 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习