内存与 OOM 诊断
目标
准确解读内存指标,区分进程实际占用的内存与仅预留的地址空间,并确认 OOM Killer 根据什么选择目标。
为什么重要
大多数内存问题的误诊,都始于查看了错误的列。应该看 free 的 available 列,而不是 free 列;应该看 top 的 RES(RSS),而不是 VIRT。OOM Killer 的评分也以 RSS 为依据——仅通过 mmap 预留 64GB 的进程甚至不会成为候选对象。此外,容器内的 free 显示的是整台主机,因此真正的上限必须从 cgroup 文件读取。本实验第 7 步之所以连‘文件不存在时要写不存在’都纳入评分,是因为在实际环境中,首先确认该文件是否存在本身就是第一步。
步骤
在 /root/mem 目录中工作。
- 从
/proc/meminfo读取MemTotal,只把 kB 数值写入/root/mem/total_kb.txt。 - 只把
MemAvailable的 kB 数值写入/root/mem/available_kb.txt。 - 在后台启动一个实际常驻内存(VmRSS)至少为 100MB 的进程,并把 PID 写入
/root/mem/hog_pid.txt。该进程必须在后续步骤中始终存活。 - 将该进程的
VmSize和VmRSS按以下两行写入/root/mem/vm.txt。vsize_kb=<값>rss_kb=<값>
- 将该进程的
oom_score_adj设为500,然后把设置之后读取的oom_score值写入/root/mem/oom_score.txt。 - 创建
/root/mem/topmem.sh <N>。把 RSS 最高的 N 个进程按<rss_kb> <comm>格式降序输出。必须正好输出 N 行。 - 将该容器的 cgroup 内存上限原样写入
/root/mem/limit.txt。如果当前环境无法读取对应文件,就写入unavailable。 - 创建
/root/mem/memguard.sh <임계값kB>。如果存在 RSS 超过阈值的进程,逐行输出over=<PID>并返回退出码 1;如果没有,则输出一行ok并返回退出码 0。没有参数时必须返回非 0。
提示
- 使用
python3 -c "import time; b=bytearray(150*1024*1024); time.sleep(9999)" &可以真正占用内存。 /proc/<PID>/status中有VmSize、VmRSS,而/proc/<PID>/oom_score与oom_score_adj也分别存在。- 内核线程没有
VmRSS项,因此第 6 步和第 8 步需要将其过滤掉。 - 常见错误 1:第 3 步只分配而不访问内存,会只增加 VmSize,VmRSS 保持不变。
- 常见错误 2:第 5 步先读取
oom_score,之后才修改 adj,会得到错误的值。
读取总内存
从 /proc/meminfo 读取 MemTotal,只把 kB 数值写入 /root/mem/total_kb.txt。
这是 /proc/meminfo 的第一项。保留 kB 单位,只写数字。
区分 MemAvailable 与 MemFree
只把 MemAvailable 的 kB 数值写入 /root/mem/available_kb.txt。
Linux 会把空闲内存用于页面缓存。有一个单独的值会把可回收容量也计算在内。
真正占用内存
在后台启动一个实际常驻内存(VmRSS)至少为 100MB 的进程,并把 PID 写入 /root/mem/hog_pid.txt。该进程必须在后续步骤中始终存活。
只分配而不访问,RSS 不会增加。必须实际填充内存。可以用 python3 创建 bytearray 后 sleep。
比较 VmSize 与 VmRSS
将该进程的 VmSize 和 VmRSS 按以下两行写入 /root/mem/vm.txt。
vsize_kb=<값>rss_kb=<값>
/proc//status 中同时包含二者。请确认预留地址空间与实际常驻内存之间的差异。
调整 OOM 分数
将该进程的 oom_score_adj 设为 500,然后把设置之后读取的 oom_score 值写入 /root/mem/oom_score.txt。
向 /proc//oom_score_adj 写入值即可。提高该值始终被允许。修改后再读取 oom_score。
列出 RSS 最高的进程
创建 /root/mem/topmem.sh <N>。把 RSS 最高的 N 个进程按 <rss_kb> <comm> 格式降序输出。必须正好输出 N 行。
收集 /proc/*/status 中的 VmRSS 并排序。内核线程没有 VmRSS 项。
找出容器的真实上限
将该容器的 cgroup 内存上限原样写入 /root/mem/limit.txt。如果当前环境无法读取对应文件,就写入 unavailable。
free 命令显示的是主机内存。真实上限位于 cgroup 文件中。如果不存在,就明确写成不存在。
内存监控脚本
创建 /root/mem/memguard.sh <임계값kB>。如果存在 RSS 超过阈值的进程,逐行输出 over=<PID> 并返回退出码 1;如果没有,则输出一行 ok 并返回退出码 0。没有参数时必须返回非 0。
通过参数接收阈值并报告超标进程。存在超标时返回退出码 1,否则输出 ok 并返回 0。