本页目录
内存看起来很满,先别急着说内存有问题
第 7 章先拦住另一个高频误判:内存占用高,就默认已经有内存压力。
Linux 这里还有一个很容易把人带偏的前提:应用先申请到的往往只是虚拟内存,不是已经兑现的物理内存。很多压力要等到真正写入、真正触发缺页以后,才会从“看起来还能撑”突然变成“怎么一下就 OOM 了”。
内存高占用,常见有几种完全不同的来源:
- 页缓存把空闲内存吃满
- 应用主动缓存和预热变多
- 分配器保留了很多内存
- 工作集真的变大了
- 回收、缺页、swap、OOM 已经开始伤人
前四种和最后一种,工程含义完全不同。
先把内存问题拆成占用、回收、缺页、交换
以后看内存,先按这个顺序判断:
1. 先看这只是占用高,还是已经开始抖
占用高本身不是结论。关键是系统有没有因为内存不够开始明显付出代价。
2. 再看回收和扫描是不是已经很重
回收变重,才说明系统开始为了维持内存平衡付出成本。
后台的 kswapd 扫描还不一定会直接把应用拖慢,但如果已经走到同步的 direct reclaim,应用线程就会被迫停下来帮系统找可回收页,这时延迟才会真的开始难看。
3. 再看缺页、swap、OOM 有没有出现
这些信号更硬。它们一出现,内存压力的可信度就高很多。
但这里也要再拆一层:minor fault 很常见,很多只是按需分配或映射建立;更值得警惕的是 major fault,它意味着这次缺页已经走到更重的路径,代价明显高得多。
4. 最后再判断是应用问题、系统缓存问题,还是配额问题
容器限额、分配器保留、页缓存增长,都会把表象变复杂。
别把缓存吃满、缺页和交换混成同一种内存问题
最常见的错法有四种:
- 看到 free 很低,就直接说内存不够
- 看到 RSS 很大,就直接说内存泄漏
- 看到 swap 用过,就直接说内存根因成立
- 在容器里只看占用,不看限额和回收行为
这些错法都把“占用”直接翻译成“压力”。
尤其是 swap,最容易把“历史上换出去过”看成“现在还在因为 swap 受罚”。真正更该盯的是当前是不是还在持续 swap in / swap out,而不是只看已经占了多少 swap。
回收代价、缺页和交换一起起来时,才更像内存压力
下面这些证据一起出现时,才更像内存已经进入关键路径:
- 回收和扫描明显变重,甚至已经走到 direct reclaim
- major fault 开始抬头,而不是只有正常的 minor fault
- 当前 swap in / swap out 持续出现,或 OOM 已经开始发生
- 延迟和这些信号同步恶化
- 降低工作集或放宽配额以后,症状明显缓解
只看到“占用高”,通常还不够。
一个最典型的错法:把缓存吃满当成内存故障
现场里经常会看到“内存几乎满了”的截图,然后所有人开始沿着泄漏、扩容、重启去想。
但很多时候,真正变大的只是页缓存或应用缓存。它说明系统在利用内存,不一定说明系统已经开始痛。
真正要追的是:
- 这些缓存到底是不是可回收的
- 回收是不是已经变得昂贵,甚至让应用线程自己下场 reclaim
- 缺页里升高的是不是 major fault
- 当前 swap 和 OOM 是不是已经开始出现
这几步不补,内存判断很容易误伤。
先把占用、回收和交换分开记
先把“占用高”和“压力大”拆开
以后看到内存数字难看,先问自己:
- 这是缓存变多了。
- 这是工作集真变大了。
- 这是回收已经开始抖了。
- 这是 major fault、当前 swap、OOM 已经开始伤人了。
占用、回收、缺页、交换要一起看
至少同时保留这四类证据:
- 占用结构
- 回收和扫描
- minor fault 和 major fault
- 当前 swap 行为或 OOM
只看单一占用数字,很难判断对不对。
别把 fault 和 swap 写成一团
缺页不是天然坏消息。很多 minor fault 只是正常分配和映射建立,真正更像性能问题的是 major fault 抬头,因为它常常意味着更重的缺页代价。
swap 也是一样。swpd 这类“已经用了多少 swap”更像历史结果;更该警惕的是系统现在是不是还在持续换入换出。前者说明系统曾经紧过,后者才更像当前性能正在被拖慢。
还有一个常被全局平均掩盖的坑: NUMA 局部干旱。整机看起来内存还很多,不代表某个节点的本地内存没被打空。多路机器上,只要单个节点先干了,应用就可能开始承受远端访问、局部回收,甚至直接在局部压力下触发 OOM。看整机大盘不够,必要时要分节点看。
把证据落到工具上
如果你已经怀疑内存开始伤业务,就别停留在概念上,直接去找对应证据:
- 怀疑只是
free低但其实是缓存吃满:先看占用结构,不要只盯总量 - 怀疑扫描和回收已经很重:看
sar -B这类能反映分页活动的指标 - 怀疑已经走到 direct reclaim:用
drsnoop这类工具抓应用线程被同步回收卡住的毛刺 - 怀疑 swap 正在伤性能:看
vmstat 1里的si、so,不要只看swpd - 怀疑内存压力已经真实拖慢任务:看
/proc/pressure/memory,比只看占用更接近“业务到底被卡了多久”
容器环境先补限额和 reclaim 证据
在容器里,先补:
- 内存限额
- reclaim 行为
- cgroup 级别的压力信号
- 页缓存是否也被算进这层限制
- OOM 触发边界
这一层不补,很多裸机直觉都会失真。尤其在容器里,页缓存也可能直接占掉 cgroup 可用内存,所以“只是缓存吃满”在裸机上也许还只是利用率高,在容器里却可能已经逼近回收、swap 甚至 OOM。
云和容器环境还有一条判断边界要提前立住: 很多环境根本没有 swap 这层缓冲带。没有 swap 时,内存耗尽不一定先经历一段明显的“系统越来越抖”,而可能是突发分配一上来就直接 OOM。别等“先看到 swap 抖动再预警”,那时可能已经晚了。
这一章最该记住的 8 个判断
free很低通常只是 Linux 在积极利用内存,不是已经证明内存不够。malloc成功不代表物理内存真的够,很多风险会拖到真正写入时才爆出来。- minor fault 常常是正常现象,major fault 更像值得追的性能证据。
swpd大说明历史上紧过,不等于现在还在被 swap 拖慢。- 真正危险的是 direct reclaim,因为它会把应用线程直接拉进回收路径。
- 内存压力是否真的伤业务,最好看 stall,而不只是看占用。
- 容器里的页缓存也会占 cgroup 配额,所以“只是缓存”未必安全。
- OOM 更像故障结果,不只是“压力有点大”的一种普通信号。
- RSS 持续变大,也不自动等于代码泄漏;共享库重复计入和分配器保留内存都会把表象抬高,必要时要看 PSS,而不是只看 RSS。
内存分清以后,接着追这几个问题
- 现在看到的难看内存数字,究竟是缓存、工作集、回收,还是配额边界?
- 当前升高的是 minor fault 还是 major fault?
- 系统只是历史上用过 swap,还是此刻还在持续 swap in / swap out?
- 应用延迟抖动时,是后台回收在忙,还是线程已经被 direct reclaim 卡住?
- 容器被打爆时,真正吃掉边界的是匿名内存,还是页缓存?
- 如果把工作集缩小一点、把限额放宽一点,症状会不会明显缓解?
内存分清以后,再看 I/O 路径哪一层先慢
第 7 章解决的是“内存高到底算不算压力”。第 8 章接着进入 I/O 软件路径,先从文件系统这一层拆开。