Linux内存Overcommit机制:三种策略、OOM Killer算法与cgroup限制行为的工程化分析

发布时间:2026/7/7 3:50:52
Linux内存Overcommit机制:三种策略、OOM Killer算法与cgroup限制行为的工程化分析 Linux内存Overcommit机制三种策略、OOM Killer算法与cgroup限制行为的工程化分析一、为什么Linux允许超额分配内存物理内存是有限资源但进程的内存请求远大于实际使用量。典型场景进程malloc了1GB但只写了100MB剩余900MB从未触碰。如果内核拒绝所有超额请求大量内存被浪费在已申请但未使用的状态。Overcommit机制允许内核在malloc时只记录分配意向物理页面在首次写入时才真正分配缺页中断触发。代价是当所有进程的意向总量超过物理内存swap时内核必须杀死某些进程来释放内存——这就是OOM Killer。二、vm.overcommit_memory的三种策略内核通过vm.overcommit_memory参数控制Overcommit行为取值0、1、2值策略名行为适用场景0heuristic内核智能判断允许合理超额但不允许明显不可能的请求通用服务器1always总是允许不做任何检查科学计算、大数据内存使用模式可预测2never不允许超额分配总量不超过overcommit_ratio%×物理内存swap关键业务、内存敏感型数据库heuristic模式的判断逻辑heuristic模式下内核计算已提交内存Committed_AS与可用内存的关系。当请求的分配量使Committed_AS超过以下阈值时拒绝阈值 物理内存 swap slab可回收内存内核不会精确计算——它允许一定量的超额因为经验表明大部分malloc不会被全部使用。但极端超额请求如一个进程申请超过可用内存总量会被拒绝。never模式的精确限制never模式使用vm.overcommit_ratio默认50计算允许的总量上限CommitLimit (物理内存 × overcommit_ratio/100) swap对于2GB物理内存、1GB swap、overcommit_ratio50的系统CommitLimit 1GB 1GB 2GB。超过2GB的malloc请求将被拒绝并返回ENOMEM。OOM触发流程flowchart TD A[进程调用malloc/mmap] -- B{overcommit_memory策略} B --|0 heuristic| C[内核智能判断] B --|1 always| D[直接允许分配] B --|2 never| E[检查CommitLimit] C -- F{合理超额?} F --|是| D F --|否| G[返回ENOMEM] E -- H{Committed_AS ≤ CommitLimit?} H --|是| D H --|否| G D -- I[记录分配意向] I -- J[进程首次写入内存页] J -- K[触发缺页中断] K -- L{物理内存swap有可用页?} L --|是| M[分配物理页 映射到进程] L --|否| N[触发OOM Killer] N -- O[计算各进程oom_score] O -- P[选择最高分进程] P -- Q[杀死选中进程] Q -- R[释放内存] R -- M style N fill:#e74c3c,color:#fff style G fill:#e74c3c,color:#fff三、OOM Killer的选择算法oom_score_adj的工程机制OOM Killer的目标是选择最该死的进程。评分逻辑oom_score 进程内存占用占比 × 1000 oom_score_adjoom_score_adj是可调的偏移值范围-1000~1000-1000进程永远不会被OOM Killer选中如sshd、系统关键服务0默认值纯按内存占比排序1000进程优先被选中如非关键的后台任务评分计算细节内核遍历所有进程对每个进程计算进程的RSS驻留内存集占系统总可用内存的百分比进程的swap占系统总swap的百分比两者相加后乘以1000得到基础分加上oom_score_adj得到最终分分数最高的进程被选中杀死/* 内核 oom_badness() 的核心逻辑简化版 */ long oom_badness(struct task_struct *p, long totalpages) { long points; long adj; adj p-signal-oom_score_adj; if (adj OOM_SCORE_ADJ_MIN) { /* -1000: 不可杀死 */ return LONG_MIN; } points get_total_mm_pages(p) * 1000 / totalpages; points adj; return points; }工程实践中的关键操作# 保护关键服务不被OOM杀死 echo -1000 /proc/$(pidof sshd)/oom_score_adj # 让非关键后台任务优先被杀死 echo 500 /proc/$(pidof log_aggregator)/oom_score_adj # 查看当前OOM评分 cat /proc/$(pidof mysqld)/oom_score四、cgroup内存限制的实际行为与Overcommit的交互cgroup v1的memory.limit_in_bytes和cgroup v2的memory.max设置了进程组的内存上限。关键问题cgroup限制与Overcommit如何交互行为规则cgroup限制是物理限制当进程组的内存使用达到limit时内核触发cgroup级别的回收回收文件页、swap出匿名页回收失败后触发cgroup OOM如果回收后仍超限内核在cgroup范围内执行OOM Killercgroup OOM与全局OOM独立cgroup内OOM不会触发全局OOM Killer只在cgroup范围内选择进程Overcommit策略仍然生效cgroup内的malloc请求仍受vm.overcommit_memory策略约束cgroup内存限制的C代码演示#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/mman.h /* * 在cgroup内存限制下测试Overcommit行为 * 编译: gcc -o oom_test oom_test.c * 运行前设置cgroup限制: * echo 100M /sys/fs/cgroup/test/memory.limit_in_bytes * echo $$ /sys/fs/cgroup/test/cgroup.procs */ #define PAGE_SIZE 4096 #define ALLOC_MB 200 /* 申请200MB超过100MB的cgroup限制 */ int main(void) { size_t total ALLOC_MB * 1024 * 1024; char *region; size_t touched 0; printf(当前进程PID: %d\n, getpid()); printf(申请内存: %d MB\n, ALLOC_MB); /* malloc利用Overcommit只记录意向不分配物理页 */ region malloc(total); if (!region) { perror(malloc失败); return 1; } printf(malloc成功Overcommit允许\n); /* 逐页写入触发缺页中断真正消耗物理内存 */ for (size_t i 0; i total; i PAGE_SIZE) { region[i] 0x42; touched PAGE_SIZE; if (touched % (10 * 1024 * 1024) 0) { printf(已写入: %zu MB\n, touched / (1024 * 1024)); } } /* 如果执行到这里说明cgroup限制未生效或Overcommitalways */ printf(全部写入完成不应到达此处\n); free(region); return 0; }运行结果预期进程在写入约100MB时被cgroup OOM Killer杀死。若Overcommit1且cgroup使用swap可能推迟OOM触发时间。cgroup v2的OOM行为配置# 设置内存硬限制超出即OOM echo 100M /sys/fs/cgroup/test/memory.max # 设置内存软限制超出后优先回收但不OOM echo 80M /sys/fs/cgroup/test/memory.low # 禁用OOM Killer超出限制后进程被挂起等待内存释放 echo 1 /sys/fs/cgroup/test/memory.oom.groupmemory.oom.group是cgroup v2的新特性设置为1时当cgroup触发OOM杀死cgroup内所有进程而非只杀最高分进程。适用于容器场景——容器内所有进程应作为整体被杀死。五、总结Overcommit三种策略heuristic(0)智能判断、always(1)无条件允许、never(2)严格限制never模式通过CommitLimit公式精确控制分配上限OOM Killer评分算法基础分内存占比×1000加上oom_score_adj偏移-1000使进程不可被杀、1000使进程优先被杀cgroup内存限制是物理级别的硬约束回收失败后在cgroup范围内触发OOMcgroup OOM与全局OOM独立运作cgroup v2新增memory.low软限制和memory.oom.group整体杀死特性更适合容器化场景的内存管理Overcommit策略与cgroup限制叠加生效cgroup限制物理使用量Overcommit策略控制malloc阶段的逻辑分配量两者分别在不同层面约束内存行为