服务器入坑指北:内存超额承诺的取舍

举报
Tulpartner 发表于 2026/11/10 08:25:26 2026/11/10
【摘要】 物理内存明明还有,新进程却申请不到;或者反过来——承诺了一堆、平时没事,某天一起兑现就雪崩;也不知道该看哪个数判断"危不危险"。 在你算比值之前,先把这条前提看清:承诺不是已用,两者必须分开看——承诺总量是"允许被申请走的地址空间",已用才是真占的物理内存。承诺可以远大于物理内存而不立刻出事——出事发生在"一起兑现"的那一刻。

物理内存明明还有,新进程却申请不到;或者反过来——承诺了一堆、平时没事,某天一起兑现就雪崩;也不知道该看哪个数判断"危不危险"。
在你算比值之前,先把这条前提看清:承诺不是已用,两者必须分开看——承诺总量是"允许被申请走的地址空间",已用才是真占的物理内存。承诺可以远大于物理内存而不立刻出事——出事发生在"一起兑现"的那一刻。
这篇给三段可直接粘贴的内容:第一段只读(一次读齐)、第二段只读(算两式)、第三段含写操作(改策略并读回,标回滚点;容器侧另算一遍)。取值用占位符,字段名、参数名与信号名照写。

承诺发生在建映射那一刻

进程申请内存的动作(mmap 建立映射)就是"承诺"发生的时点——此时还没真占页。
所以"申请成功"不代表"用得起来";而 MAP_NORESERVE 是"显式绕过承诺检查"的那面旗——它是超额承诺的危险用法,不是推荐做法:它表示不为该映射保留交换空间,也就是说你把本该由内核拦下的那次检查自己关掉了。

一次读齐块

cat /proc/meminfo                            # 一次拿到承诺与内存的全部读数
free -h                                      # 物理内存与交换(人类可读)
vmstat -s                                    # 汇总视图里再看一遍内存与交换
sysctl vm.overcommit_memory                  # 现读承诺策略当前值

判据:这一块里每个数都要认领自己是"承诺"还是"已用"——

  • 承诺侧:Committed_AS 是已承诺地址空间总量,CommitLimit 是当前可承诺上限;
  • 容量侧:MemTotal 是物理内存总量,SwapTotal 是交换区总量;
  • 可用侧:MemAvailable 是可供新用途使用的估计值——它与"空闲量"不是一回事。

算两式

第一式:承诺比 ≈ 承诺总量 / (物理内存 + 交换),即 Committed_AS / (MemTotal + SwapTotal)。
算一遍(数值为演示值):Committed_AS 按 21474836480 字节(20 GiB)、MemTotal 按 17179869184(16 GiB)、SwapTotal 按 2147483648(2 GiB)⇒ 分母 17179869184 + 2147483648 = 19327352832;承诺比 = 21474836480 / 19327352832 ≈ 1.11。
比值大于 1 意味着什么:已经承诺出去的量超过了物理内存与交换的总和——它不必立刻出事(承诺不等于已用),但它正是"什么时候危险"的那个量。
第二式:严格档下的上限 ≈ 交换总量 + 物理内存 × 计入比例,即 SwapTotal + MemTotal × vm.overcommit_ratio。
接着算(同一组数往下走):计入比例按 0.5 ⇒ 2147483648 + 17179869184 * 0.5 = 2147483648 + 8589934592 = 10737418240(字节,10 GiB)。拿它与 CommitLimit 的现读值对照:对得上,说明当前上限就是按这一套算的;对不上,说明走的是另一档。
策略有三档:启发式(按经验允许一定超额)· 总是允许(最危险,只适合明确知道自己在做什么的场景)· 严格(按"交换 + 物理内存 × 计入比例"算上限)。另有以 KB 计 vm.overcommit_kbytes 的形态——用它替代比例时,上面的加法式换成分量直接相加。

危不危险看两个量

  • 承诺比(上面第一式):系统性超额程度——它回答"这台机器整体承诺出去多少";
  • 承诺与已用的差(Committed_AS 与 MemAvailable 的相对关系):差额越大,兑现时的冲击越大。
    判据:只看"还剩多少物理内存"会漏掉这一整类风险——MemAvailable 看着还有,但承诺已经排到很后面了,这就是"物理内存还有、新进程却申请不到"的成因。

策略与容器侧块

sysctl vm.overcommit_memory                  # 现读策略档:三档取值语义以现读为准
cat /sys/fs/cgroup/memory.max                # 控制组硬上限(前提:须为统一层级 cgroup 环境,路径以现读为准)
cat /sys/fs/cgroup/memory.current            # 控制组当前用量
dmesg --level=err                            # 只读错误级内核消息(前提:受限环境需特权或调 kernel.dmesg_restrict)

两本账不可互替:容器里的 memory.max / memory.current 是控制组侧的账,与宿主侧的 Committed_AS / CommitLimit 不是同一本——不得用宿主的读数去解释容器内的表现(反之亦然)。容器场景要在控制组那两个量上另算一遍。
dmesg --level=err 的前提要写在读之前:受限环境里它敲了没输出——成因是 kernel.dmesg_restrict 决定非特权用户能不能读内核日志(是它决定读权限,不是反过来);所以**“没输出"不等于"没发生”**。
承诺失衡的最终表现是不可捕获的强杀:SIGKILL 不可被捕获或忽略——多个进程一起被这样杀掉,通常就是承诺一起兑现的那一刻。

取舍:放开承诺,还是收紧承诺

  • 放开:换更高的部署密度;代价是把风险推迟到兑现时刻,而且兑现时是"多个进程一起失败";
  • 收紧:换可预测性;代价是内存利用率下降、可能提前拒绝本来能跑的任务。
    两条代价都要写出来。判据:这取决于"你能不能接受多个进程同时失败"——能接受就用放开换密度,不能接受就用收紧换确定性。改策略属高风险写操作,且影响整机、影响面最大——先记原值、留回滚形态。
    策略侧改与读回:
sysctl -w vm.overcommit_memory=<取值>         # 改策略档(高风险写操作:影响整机,需回滚点)
sysctl -w vm.overcommit_ratio=<百分比>         # 严格档下的计入比例(同上,先记原值)
cat /proc/meminfo | grep -E 'Committed_AS|CommitLimit|MemTotal|SwapTotal'   # 读回:四个量各是多少
free -h                                      # 读回:物理内存与交换

现象到动作的判定表

现象 结论 动作
物理内存还有但申请失败 风险已经在兑现 算承诺比与"承诺与已用的差",不要只看 MemAvailable
承诺比 > 1 但一切正常 这是延后风险,不是故障 记下这个比值作为基线,定期复查
多个进程同时被杀 承诺一起兑现 看 SIGKILL 的现场,收紧承诺或降密度
容器内表现与宿主读数不一致 两本账 在 memory.max / memory.current 上另算一遍
dmesg 读不到 受限,不是没发生 核 kernel.dmesg_restrict,改用别只读入口

改完怎么验证:每项一条判据

  • 两个式子都算得出(承诺比、严格档上限);
  • 承诺比与"承诺与已用的差"能分别解释一个现象;
  • 策略档位能读出来,并与上限式对照;
  • 容器侧与宿主侧分开算过;
  • 有原值记录与回滚形态。

这篇不解决什么

  • 讲 cgroup v2 内存上限与 OOM 取证的那篇管限制层(上限设多少、被杀了怎么归因);本篇管"承诺"这一层——为什么"申请得到"和"用得起来"是两件事。判定入口:看的是"被杀之后"还是"还没申请到"。
  • 讲内存泄漏的观察与处置顺序的那篇管占用趋势;本篇管"承诺与已用的比"——一个看时间维度、一个看分配维度。
  • 讲内存口径打架时信谁的那篇是读数口径之间的取舍;本篇讲的是承诺与已用这两个口径之间的关系。

踩坑清单

  • 只看"还剩多少物理内存":会漏掉"承诺排到很后面"这一整类风险。
  • 把"申请成功"当成"用得起来":承诺发生在建映射那一刻,页还没占。
  • 把 MAP_NORESERVE 当推荐做法:它是绕过承诺检查、把本该拦下的动作放行。
  • 把 MemAvailable 当成"承诺还没用完":可用估计与承诺余量是两个量,一个涨不代表另一个有空间。
  • 改策略不记原值:影响整机,回滚没有依据。
  • 把 dmesg 无输出当成没发生:受 kernel.dmesg_restrict 管。
  • 以为"承诺比高就一定会崩":它是风险,不是故障。
  • 把"交换很大"当成安全的理由:严格档正是拿交换与计入比例一起算上限的。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。