小内存服务器排查内存占用与 OOM 的思路
一台 2G 内存的机器,free -h 显示还有几百兆可用,某个进程却被杀了。
这类情况我遇到过好几次,每次都是先在三个不同的「内存口径」之间绕一圈,
最后才想起来该看哪个。这里把顺序记一下。
第一步:分清 free 的三列
free -h
total used free shared buff/cache available
Mem: 1.9Gi 1.2Gi 86Mi 15Mi 790Mi 710Mi
Swap: 6.0Gi 1.2Gi 4.8Gi
free:完全没被用的。这个数字小不代表内存紧张,因为buff/cache是可回收的。used:已经被进程实际占用的。available:这个才是关键。它估算的是「新程序还能申请到多少」, 已经把可回收的缓存算进去了。
判断内存是否紧张,看 available,不要看 free。
available 掉到总内存的 10% 以下,就该处理了。
第二步:确认 swap 是不是在硬撑
上面那台机器 available 还有 710Mi,看起来还行,
但 swap 已经用掉 1.2G。这意味着有大量页被换出到了磁盘,
进程虽然没被杀,实际响应速度会明显变慢。
看 swap 的换入换出速率,比看总量更有意义:
vmstat 1 5
# si/so 两列持续不为 0,说明正在频繁换页,性能已经在受影响
第三步:找真正的占用者
ps aux --sort=-%mem 按内存排序,但要注意 RSS 会把共享库重复计算,
多进程程序(比如浏览器内核、Node 的 worker)加起来的数字会虚高。
更准确的是按进程组看:
# 按 RSS 排序,取前 10
ps -eo pid,ppid,rss,comm --sort=-rss | head -11
# 同一个服务的所有进程合计(按进程名)
ps -eo rss,comm | awk '/nginx/{s+=$1} END{print s/1024" MB"}'
第四步:如果是被 cgroup 杀的,dmesg 会写明
这是最容易被忽略的一点。进程被 OOM killer 杀掉时,
如果是被某个 cgroup 的内存上限杀的,dmesg 里会带上
constraint=CONSTRAINT_MEMCG:
dmesg -T | grep -i -E 'oom|killed process' | tail -20
这时候去调大整机的 swap 是没用的——限制在 cgroup 那一层, 被杀的是那个 scope 里的进程,不是全系统缺内存。
第五步:看内存压力,而不是看余量
available 是个瞬时快照,压力是持续的过程。
PSI(Pressure Stall Information)能直接告诉你「有多少时间在等内存」:
cat /proc/pressure/memory
# some avg10=8.50 avg60=2.40 avg300=0.55
# full avg10=7.76 avg60=2.22 avg300=0.51
full 那一行表示「所有可运行任务都被内存卡住」的时间占比。
如果 full avg10 长期在两位数,说明机器已经在靠换页硬撑了,
这时候该做的是减负载,而不是继续往上堆服务。
实际处理顺序
- 看
available和 PSI 的full,判断是不是真的紧张 - 看 swap 的
si/so,判断是不是在频繁换页 - 按 RSS 找占用最大的进程组,确认有没有异常膨胀
- 如果进程是被杀的,去
dmesg确认是全局 OOM 还是 cgroup OOM - 最后才考虑加内存或加 swap,前面几步往往能直接定位到问题进程
小内存机器上,比起「加 swap 续命」,更有效的是给可能吃内存的操作设一个 cgroup 上限。 让它在自己的 scope 里被杀,而不是把整机拖进全局 OOM。
← 返回笔记列表