Linux 内存飙高怎么排查?

2026年 阅读约 8 分钟 面试指南 · Linux/Docker面试

Linux面试题:内存排查,free输出解读、buffer与cache区别、available含义、top与load average、OOM killer机制、内存泄漏与Java进程排查实战。

一句话总结

排查口诀:先 free 看 available(不是看 free!),再 top 找到吃内存的进程,再往下钻。三个必考概念:buffer/cache 是内核缓存可回收(free 命令显示的大不代表有问题)、available 才是真正可用内存、load average 高不一定内存问题(可能是 CPU 或 IO 等待)。

初级理解

free -h 输出逐列解读(面试必考):

$ free -h total used free shared buff/cache available Mem: 15Gi 8.2Gi 1.1Gi 210Mi 6.0Gi 6.8Gi Swap: 8.0Gi 0B 8.0Gi
  • free 少 ≠ 内存不足:buff/cache 是内核用空闲内存缓存磁盘数据(读文件快),需要时立即回收给应用
  • available 才是答案:估算"不触发 swap 的前提下还能给新进程多少内存"——判断内存是否紧张看它
  • Swap used 高:说明物理内存真不够过,系统已经在拿磁盘当内存(性能雪崩),要警惕

top 必看行:第一行 load average(1/5/15 分钟平均负载),进程区按 Shift+M 按内存排序。RES 是进程实际占用的物理内存(排查看它,不是 VIRT)。

中级深入

load average 的正确理解:单位时间内可运行 + 不可中断睡眠(D 状态)的平均进程数。经验判断:load 持续 > CPU 核数说明排队了;但要结合 vmstat 1 区分是 CPU 忙(us/sy 高)还是 IO 等待(wa 高 → load 高但 CPU 空闲,去查磁盘)。

OOM(Out Of Memory)机制:物理内存+Swap 都耗尽时,内核的 oom_killer 按 oom_score 挑选进程杀掉。特征:进程"无故消失"、dmesg 里有 "Killed process" 记录。排查:

dmesg -T | grep -i 'killed process' # 确认 OOM 记录与被杀进程 grep -r 'oom_score' /proc/<pid>/ # 查看进程 oom_score # 防护:关键进程 echo -1000 > /proc/<pid>/oom_score_adj(禁止被 OOM 杀)

Java 进程特别说明(后端面试高频):JVM 的堆内存只占进程内存的一部分——堆外内存(DirectBuffer、Metaspace、线程栈、JIT 代码缓存、glibc malloc 碎片)都算在 RES 里。容器里"Xmx 设了 2G 但进程 RSS 4G 被杀"就是典型:堆外也要算预算,容器环境用 -XX:MaxRAMPercentage 并开启 NativeMemoryTracking 分析。

高级拓展

内存泄漏排查工具链:

  • 趋势判断:监控 RES 随时间稳步上涨且不回落(GC 后也不降)→ 泄漏特征
  • 进程内部分析:pmap -x <pid> 看内存段分布,找异常大的匿名段
  • C/C++:valgrind(离线)/ jemalloc profiling(在线采样)
  • Java:jmap -histo:live <pid> 看对象直方图;dump 后 MAT 分析支配树;NMT(NativeMemoryTracking=summary)看堆外
  • 内核态:slabtop 看内核 slab 占用(dentry/inode 缓存泄漏会吃光内存)

Swap 的调优观点(体现经验):数据库类应用建议 swappiness=1(尽量不用 swap,IO 延迟不可控);通用应用 10~60。swap 高不等于要立即重启——先看是不是 buff/cache 回收压力,很多时候清缓存/限流即可恢复。

注意:容器环境内存限制是 cgroup 级的——top 看宿主机正常不代表容器没事,要用 cat /sys/fs/cgroup/memory/memory.usage_in_bytes 或 docker stats 看容器视角,超限直接触发 cgroup OOM。

实战场景

场景一:告警"内存使用率 90%",标准排查流程

1. free -h → available 还有多少?swap 用了吗? 2. top (Shift+M) → 哪个进程 RES 最大? 3. 分叉: - buff/cache 占大头 → 正常缓存,确认业务无感知即可忽略 - 某进程 RES 异常 → 是 Java?jmap 看堆。是 Redis?看 maxmemory 配置 - 多进程都涨 → 共性问题:缓存过期策略/流量上涨/泄漏 4. 紧急处置:限流/重启嫌疑进程(先 dump 再杀)/ 扩容

场景二:Java 服务容器里频繁被杀

# 现象:docker 里 JVM 运行几小时被 OOM Kill,dmesg 见 oom-kill # 原因:容器 limit 2G,但 -Xmx2g 只管堆——Metaspace+线程栈+DirectBuffer 堆外超限 # 解决: -XX:MaxRAMPercentage=70.0 # 让 JVM 感知容器限制,堆只占 70% -XX:MaxDirectMemorySize=256m # 显式限制堆外 # 复盘:压测验证 RSS 峰值 < limit × 80%

场景三:系统卡但 CPU 不高

vmstat 1 # r 列高:CPU 排队 → CPU 问题 # wa 列高(>30):IO 等待 → 磁盘问题(load 高是 D 状态进程贡献的) # si/so 持续非 0:在换页 → 内存不足,去查内存 # b 列高 + wa 高:IO 阻塞 → iostat -x 1 找 %util 接近 100% 的盘

面试模拟

Q:free 命令里 free 很小、buff/cache 很大,需要处理吗?

A:看 available 列——它综合了可回收的 buff/cache,才是"真正可用"。Linux 设计就是"空闲内存拿来做缓存",free 小是正常状态。需要处理的情况只有:available 持续很低、swap 在涨、或某进程异常吃内存。手动 echo 3 > drop_caches 清缓存是面试雷点——生产上清了除了让下次读盘更慢没有任何收益。

Q:load average 10,CPU 使用率 5%,说明什么?

A:典型"load 高但 CPU 空闲"——load 里的贡献者大概率是不可中断 D 状态进程在等 IO。验证:vmstat 看 wa(IO 等待占比)和 b 列(D 状态数),ps -eo stat 找 D 进程;再用 iostat -x 看哪块盘 %util 打满。结论:不是 CPU 问题,是存储/IO 问题,按磁盘排查路径走。

Q:怎么区分"内存泄漏"和"内存正常增长"?

A:看趋势与可回收性。正常增长:随流量/缓存水位涨,有上限、GC 或到期淘汰后回落。泄漏:单调上涨不可逆(Java:Full GC 后 old 区基线持续抬升;C:RSS 永不释放)。做法是画 RES/堆使用率的长周期曲线 + 压测验证(恒定流量跑 24h,看是否收敛到平台期)。Java 再用 MAT 对比两个时点的 dump 找增长对象簇。