首页 / 内容指南 / 当前文章

systemd 的 MemoryMin、MemoryLow、MemoryHigh、MemoryMax、MemorySwapMax、OOMPolicy、OOMScoreAdjust 和 ManagedOOM 有什么区别?

发布于 2026-08-26 · Content Fleet 编辑部

systemd 的 MemoryMin、MemoryLow、MemoryHigh、MemoryMax、MemorySwapMax、OOMPolicy、OOMScoreAdjust 和 ManagedOOM 有什么区别?

八个选项快速对比

先理解 systemd 的 cgroup 层级

每个 Service 通常位于某个 Slice 下,例如 myapp.service 可能在 app.slice,Slice 又在系统根 cgroup 下。内存控制是层级化的:子服务的有效上限不能突破父 Slice 的上限,父级压力也会影响所有子级。

-.slice
└─app.slice (MemoryMax=8G)
  ├─api.service (MemoryMax=4G)
  └─worker.service (MemoryMax=6G)

两个子级声明之和可以超过父级,因为限制不是预留配额;但它们同时高负载时会在父级 8G 边界竞争。要判断有效值,应查看 systemctl showsystemd-cgls、对应 /sys/fs/cgroup 文件和父级配置,而不是只读 Service 文件。

systemd 选项在统一 cgroup v2 层级上语义最完整。旧 cgroup v1、混合模式或旧 systemd 版本可能缺少部分能力,部署前要确认内核、systemd 与 hierarchy。

MemoryMin:硬保护内存

MemoryMin= 为 Unit 配置最低内存保护,对应 cgroup v2 memory.min。在有效保护范围内,内核应尽量不从该 cgroup 回收内存,即使系统面临压力。

它不是“至少分配这么多内存”的预留池。服务没有实际使用时,不会凭空占用;保护只作用于已使用内存。父级也必须向子级提供相应保护,否则子级声明可能无法全部兑现。

过度配置 MemoryMin 很危险。如果多个服务的硬保护之和接近或超过可用内存,系统没有足够可回收对象,可能更快进入全局 OOM。它适合真正关键且工作集可测量的服务,不适合给所有 Unit 统一设置很大值。

DefaultMemoryMin= 可在 Slice 层为子 Unit 提供默认值,但显式子级配置与层级分配仍需核对。

MemoryLow:尽力保护内存

MemoryLow= 对应 memory.low,也是对已使用内存的保护,但属于 best-effort。系统有足够未保护内存可回收时,会优先回收其他 cgroup;当所有可回收内存都受保护或压力极端时,Low 保护仍可能被突破。

它适合表达相对业务优先级,例如让数据库缓存比批处理 Worker 更不容易被回收。与 MemoryMin 相比,MemoryLow 更不容易把系统逼入无可回收空间。

MemoryLow 不是性能保证。被保护服务仍可能因 CPU、IO、父级 MemoryHigh、Swap 或自己的 MemoryMax 变慢。要同时监控 memory.events 中 low 相关事件、工作集、PSI 和延迟。

MemoryHigh:主要的压力与节流边界

MemoryHigh= 对应 memory.high。当 cgroup 使用量超过该值,内核会对其施加强烈回收压力,并让触发分配的进程承担节流成本。它的目标是把服务推回可控工作集,而不是立即杀进程。

MemoryHigh 可以短暂或在特定情况下被超过,因此不是安全硬边界。systemd 官方建议常把它作为主要控制手段,再用更高的 MemoryMax 作为最后防线。

例如:

[Service]
MemoryHigh=2G
MemoryMax=3G

服务越过 2G 后会感受到回收与延迟,若仍增长并在 3G 附近无法回收,才可能触发 cgroup OOM。这样比直接在 2G 设置硬上限更有机会让应用降载、释放缓存或暴露性能退化。

监控不能只看 RSS。cgroup memory.current 包括由该 cgroup 记账的匿名内存、页面缓存等。高回收会增加 CPU、major fault、IO 与尾延迟,需要结合 memory.statmemory.events 和 PSI。

MemoryMax:最后的硬上限

MemoryMax= 对应 memory.max。当 cgroup 达到上限且无法通过回收满足新分配时,内核在该 cgroup 内触发 OOM,并选择进程终止。

它不是平滑限速器。应用可能在接近边界前运行正常,然后某次分配突然失败或进程被杀。为了给服务可观察的降级区,应把 MemoryHigh 设在更低位置。

MemoryMax=infinity 表示本 Unit 不额外设置此硬上限,但父 Slice 或系统物理内存仍是边界。使用百分比选项时,基准和版本支持要查 systemd 文档。

服务包含多个进程时,OOM 可能只杀一个进程,留下不一致的其余进程。OOMPolicy=kill 或适当的 cgroup OOM group 行为可以让整个 Unit 一致失败,但要结合应用架构测试。

MemorySwapMax:限制 Swap,而不是内存总量

MemorySwapMax= 对应 memory.swap.max,限制 cgroup 可使用的 swap。它不包含 RAM 上限,也不能替代 MemoryMax。

MemorySwapMax=0 可禁止该 Unit 使用 swap,但可能增加内存压力和 OOM 风险。低延迟服务可能希望避免换出,批处理任务则可能容忍 swap 以降低被杀概率。选择应基于工作负载和磁盘性能。

主机没有启用 swap 时,设置较大 MemorySwapMax 不会创造 swap。父 cgroup 的 swap 上限、zram/zswap 和内核策略也会影响有效行为。

需要注意 cgroup v2 的 memory.maxmemory.swap.max 分别控制内存和 swap;不要套用旧 cgroup v1 memory.memsw 的总和语义。

OOMPolicy:进程被杀之后 Unit 怎么办

OOMPolicy= 是 systemd Service 的生命周期策略,不是内存阈值。它决定某个服务进程被内核 OOM Killer 终止后,systemd 如何对待 Unit 中其余进程。

常见策略包括 continuestopkill,具体默认值会受 Service 类型和 Delegate= 等配置影响:

  • continue:记录 OOM,但让剩余进程继续;
  • stop:停止该 Unit;
  • kill:终止 Unit cgroup 中其余进程,使失败具有整体性。

多进程数据库、Worker Pool 或代理若丢失关键子进程后无法保证一致性,kill 往往比留下半残服务更可预测。但 sidecar 或可独立恢复的 Worker 可能需要不同策略。

OOMPolicy 不决定是否自动重启。重启还由 Restart=、退出原因和 StartLimit 控制。应测试 OOM 退出是否匹配预期 Restart 策略,避免高速重启循环。

OOMScoreAdjust:全局内核 OOM 的相对倾向

OOMScoreAdjust= 设置进程的 oom_score_adj,影响内核在全局 OOM 时选择牺牲者的相对概率。较高值更容易被杀,较低值更受保护;具体范围和权限受 Linux 规则约束。

它不保证某进程绝对不会被杀,也不限制内存使用。把所有关键服务都设为极低值,只会把风险推给其他进程,甚至留下系统没有合适牺牲者。

在 cgroup MemoryMax 触发的局部 OOM 中,候选范围已经受 cgroup 限定;OOMScoreAdjust 的作用与全局场景不能简单等同。实际选择还取决于内核版本、进程内存和 cgroup 配置。

不要用 OOMScoreAdjust 替代内存预算。正确做法是给工作负载设置 High/Max、监控压力,并为全局 OOM 设计合理优先级。

ManagedOOM:systemd-oomd 提前干预

systemd-oomd 是用户态 OOM 管理器,利用 cgroup v2 与 PSI 等信号,在内核陷入全局 OOM 之前选择并终止 cgroup。ManagedOOMMemoryPressure= 可让 Unit/Slice 参与内存压力策略,ManagedOOMSwap= 可让其参与 swap 使用策略。

它与内核 OOM Killer 是两套机制。systemd-oomd 可能在 MemoryMax 尚未触发时,因持续压力或全局 swap 条件杀掉某个后代 cgroup;日志来源和诊断路径也不同。

ManagedOOM 配置通常放在 Slice 上,让 oomd 从一组后代中选择牺牲者。若所有工作负载都在同一不可区分层级,选择结果可能不符合业务优先级。应利用 Slice、ManagedOOMPreference= 和保护设置表达重要性,并查看目标版本文档。

systemd-oomd 不是泄漏修复器。它只能在压力下选择终止对象。应用仍需限制队列、缓存和并发,并输出内存剖析。

MemoryPressureWatch 与 PSI 的关系

Linux Pressure Stall Information(PSI)统计任务因内存等资源等待而停顿的时间。高 memory PSI 表示工作负载花大量时间等待回收或内存可用,不只是“使用率高”。

systemd 可以向支持协议的服务传递内存压力通知,使应用主动释放缓存或降载。是否支持及具体选项取决于 systemd 版本和应用实现。

只按 memory.current 百分比报警会漏掉“使用不高但回收抖动”的情况。应同时看:

  • memory.currentmemory.peak
  • memory.events 的 high、max、oom、oom_kill;
  • memory.pressure 的 some/full;
  • swap 当前值与事件;
  • 服务延迟、缺页和 IO。

cgroup OOM 与全局 OOM 如何区分

cgroup OOM 是某个 MemoryMax 边界无法满足分配,影响候选通常局限在该 cgroup。全局 OOM 是整个系统或 NUMA 节点等范围缺乏可用内存,内核在更大候选集中选择进程。

systemd-oomd 的杀进程属于用户态提前干预。三者都可能表现为进程收到 SIGKILL,但证据不同:

  • memory.eventsoom/oom_kill 增长指向 cgroup OOM;
  • 内核日志显示 Out of memory/Killed process 指向内核决策;
  • journal 中 systemd-oomd 的 kill 记录指向用户态策略;
  • Unit 的 Result=oom-kill 与 OOMPolicy 反映 systemd 观察结果。

容器内部日志可能看不到宿主内核或 oomd 记录,需在宿主与容器两侧联合调查。

一套推荐的配置思路

先测量正常工作集与峰值,再设定层级预算。示例不是通用数值:

[Service]
MemoryLow=1G
MemoryHigh=2G
MemoryMax=3G
MemorySwapMax=512M
OOMPolicy=kill
Restart=on-failure

MemoryLow 表达相对保护,High 提供压力区,Max 是最后防线,SwapMax 控制换出,OOMPolicy 保证多进程一致失败。部署前必须验证父 Slice 有足够预算,并在压力测试中观察延迟和事件。

关键服务可获得适度保护,批处理服务放入可牺牲 Slice,并由 ManagedOOM 策略管理。不要让同一主机所有服务都自称最高优先级。

一套可执行的测试流程

1. 用 systemd-analyze verify 检查 Unit 语法和目标版本支持。

2. 用 systemctl show 查看 EffectiveMemory*、ControlGroup、OOMPolicy 等属性。

3. 记录父子 Slice 的 memory.* 文件,确认层级预算。

4. 在隔离测试机逐步增加匿名内存和页面缓存,观察 MemoryHigh 节流。

5. 继续增长到 MemoryMax,确认 memory.events、journal 与 Unit Result。

6. 对多进程服务验证 OOMPolicy=continue/stop/kill 的差异。

7. 启用 swap 场景测试 MemorySwapMax=0 与有限值的延迟和 OOM。

8. 单独测试 systemd-oomd 压力策略,确认牺牲 Slice 与重启行为。

不要在未隔离的生产主机直接运行内存耗尽测试。cgroup 配置错误可能影响同一 Slice 的其他服务。

常见误区

  • 把 MemoryMin 当作预先保留并立即占用的 RAM。
  • 认为 MemoryLow 在极端压力下绝不会被回收。
  • 把 MemoryHigh 当成严格不可突破的硬上限。
  • 只设置 MemoryMax,没有提供可观察的 High 压力区。
  • 把 MemorySwapMax 当作 RAM+Swap 总上限。
  • 认为 OOMPolicy 会阻止 OOM 或自动重启服务。
  • 用 OOMScoreAdjust 代替 cgroup 内存预算。
  • 混淆内核 OOM、cgroup OOM 和 systemd-oomd Kill。

FAQ

1. MemoryHigh 和 MemoryMax 应该设成同一个值吗?

通常不建议。High 应提供提前回收和节流区,Max 作为更高的最后防线。两者相同会减少应用暴露压力和降级的空间。

2. MemoryMin 会立即占用指定内存吗?

不会。它保护实际使用量,不是预分配。服务未使用的部分不会从系统中扣走。

3. MemoryLow 能保证数据库缓存不被回收吗?

不能绝对保证。它是尽力保护;当未保护内存不足时仍可能回收。需要结合父级保护和系统容量。

4. MemorySwapMax=0 就不会 OOM 吗?

恰恰可能更容易 OOM,因为服务不能把匿名页换出。它可降低 swap 延迟,但要有足够 RAM 和合理 Max/High。

5. OOMPolicy=kill 是让内核更容易杀服务吗?

不是。它决定某个服务进程已被 OOM Kill 后,systemd 是否清理其余进程,不是牺牲者选择权重。

6. OOMScoreAdjust=-1000 能绝对防止服务被杀吗?

它会强烈保护进程,但不能作为整体可用性保证,也可能把系统风险转移给其他关键进程。cgroup 与 oomd 还有独立策略。

7. systemd-oomd 和内核 OOM Killer 哪个先发生?

oomd 的目标是在持续压力或 swap 条件下提前行动,但是否先发生取决于配置和压力速度。两者都应监控。

8. 子 Service 的 MemoryMax 能超过父 Slice 吗?

可以声明更大值,但有效使用仍受父级边界约束。父 Slice 先达到上限时,多个子服务会一起竞争并可能受影响。

结论

MemoryMin/Low 是保护,MemoryHigh 是压力区,MemoryMax 是硬边界,MemorySwapMax 限制 swap;OOMPolicy 处理 OOM 后的 Unit 一致性,OOMScoreAdjust影响内核全局选择,ManagedOOM让 systemd-oomd提前按 cgroup 压力干预。正确配置必须结合父子层级、工作集测量、PSI 和真实 OOM 演练。

参考来源

1. systemd upstream, systemd.resource-control: https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html

2. systemd upstream, systemd.service — OOMPolicy: https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html

3. systemd upstream, systemd.exec — OOMScoreAdjust: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html

4. systemd upstream, systemd-oomd: https://www.freedesktop.org/software/systemd/man/latest/systemd-oomd.html

5. systemd upstream, oomd.conf: https://www.freedesktop.org/software/systemd/man/latest/oomd.conf.html

6. Linux Kernel Documentation, Control Group v2 — Memory Interface: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html

7. Linux Kernel Documentation, Pressure Stall Information: https://www.kernel.org/doc/html/latest/accounting/psi.html