4. 内存性能分析:内存使用率、Swap、缺页异常、内存泄漏

内存问题,说实话,是推理系统里最难排查的一类。CPU 跑慢了你能看到,网络延迟高了你能测到,但内存泄漏——它就像温水煮青蛙,等你发现的时候,进程已经被 OOM Killer 干掉了。

我个人习惯,排查内存问题先看三个层面:系统层面(free、vmstat)、进程层面(/proc/pid/status)、代码层面(valgrind)。今天咱们就按这个顺序来捋一遍。

核心知识点速览

  • 内存使用率:别只看 %,要看实际可用量
  • Swap:交换分区用多了,性能直接崩
  • 缺页异常:major fault 是性能杀手
  • 内存泄漏:积少成多,最终 OOM
内存性能分析体系 内存使用率 free -h / top Swap 分析 swapon / vmstat 缺页异常 major / minor fault 内存泄漏 valgrind / asan 常用工具:free · vmstat · valgrind · /proc/meminfo · top 排查路径:系统级 → 进程级 → 代码级 核心原则:先看系统容量,再看进程行为,最后定位代码 避免盲目优化,用数据说话

4.1 内存使用率——别被百分比骗了

很多人一上来就敲 free -h,看到用了 80% 就慌了。其实 Linux 的内存管理机制决定了「已用」不等于「真的不够用」。

我建议你这样看:

# 第一眼
free -h

# 真正有用的
free -h | grep "Mem" | awk '{print $7}'
# 或者
cat /proc/meminfo | grep MemAvailable

为什么?因为 Linux 会把空闲内存拿去做缓存(cache/buffer)。你看到 80% 已用,可能其中 40% 是 cache,随时可以回收给应用程序用。

指标含义重点关注
total物理内存总量硬件容量
used已使用(含 cache)别只看这个
free完全空闲太低说明紧张
available实际可用这才是关键
buff/cache缓存和缓冲区可回收资源

我的经验:available 低于 total 的 10%,或者持续下降,才需要警惕。我曾经遇到一个推理服务,free 显示只剩 200MB,但 available 还有 6GB——其实 cache 占了大部分,完全没问题。

4.2 Swap——性能的隐形杀手

Swap 是什么?说白了就是把磁盘当内存用。你想想看,内存访问是纳秒级,磁盘是毫秒级——差了三个数量级。一旦进程开始频繁使用 Swap,性能直接跳水。

怎么查?

# 查看 Swap 使用情况
free -h | grep Swap

# 更详细的
swapon --show

# 实时监控 Swap 变化
vmstat 1 5

vmstat 输出里,重点关注 si(swap in)和 so(swap out)这两列。如果这两个值持续不为 0,说明内存真的不够用了。

⚠️ 避坑指南:我曾经在生产环境上遇到过,某台机器的 Swap 用了 2GB,但 free 显示内存还有 30% 空闲。查了半天才发现,是内核参数 vm.swappiness 设成了 60,导致内核过早地把不活跃的内存页换出去了。对于推理服务,我建议把 swappiness 调到 10 甚至 0。

# 临时调整
sysctl vm.swappiness=10

# 永久生效
echo "vm.swappiness=10" >> /etc/sysctl.conf

4.3 缺页异常——major fault 是性能杀手

缺页异常分两种:minor fault 和 major fault。

  • minor fault:页在内存中,只是还没映射到进程地址空间。很快,基本没影响。
  • major fault:页不在内存中,需要从磁盘读入。很慢,一次 major fault 可能耗时 10ms+。

怎么查?

# 查看进程的缺页情况
cat /proc/[pid]/status | grep -E "VmPeak|VmRSS|VmSwap"

# 用 ps 看 major fault
ps -o pid,min_flt,maj_flt,cmd -p [pid]

# 或者用 top 看
top -p [pid]  # 按 f 选择 nFLT 列

我一般用 vmstat -s 看系统级别的缺页统计:

vmstat -s | grep -E "page|fault"

实战经验:有一次我排查一个推理服务的性能抖动,发现每隔 30 秒就会出现一次 200ms 的延迟尖刺。用 perf top 一看,大量时间花在 do_major_fault 上。再查,原来是模型权重文件被 mmap 到了内存,但内存不够,部分页被换出了。解决方案很简单:加内存,或者把 swappiness 调低。

4.4 内存泄漏——用 valgrind 精准定位

内存泄漏是 C/C++ 推理框架的老大难问题。Python 有 GC 还好,但底层调用的 CUDA 库、自定义算子,一不小心就漏了。

我常用的工具是 valgrind,虽然它会让程序跑慢 10-20 倍,但定位问题非常准。

# 基本用法
valgrind --leak-check=full --show-leak-kinds=all ./your_inference_bin

# 如果只想看泄漏摘要
valgrind --leak-check=summary ./your_inference_bin

# 生成详细日志
valgrind --leak-check=full --log-file=valgrind.log ./your_inference_bin

输出里重点关注这几类:

  • definitely lost:确定泄漏了,必须修
  • indirectly lost:间接泄漏,通常跟着 definitely lost 一起出现
  • possibly lost:可能泄漏,需要进一步确认
  • still reachable:还没释放但还能访问到,通常问题不大

我的小技巧:valgrind 跑起来太慢怎么办?可以用 AddressSanitizer(ASan),编译时加上 -fsanitize=address 就行,性能开销只有 2-3 倍。我一般在开发环境用 ASan,CI 里跑 valgrind 做全量检查。

# 编译时启用 ASan
g++ -fsanitize=address -g -O1 -o inference inference.cpp

# 运行时自动检测泄漏
./inference

4.5 实战排查流程

嗯,说了这么多,咱们总结一个排查流程。遇到内存问题,我一般按这个顺序来:

  1. 先看系统级:free -h 看 available,vmstat 1 看 si/so
  2. 再看进程级:top -p [pid] 看 RES 和 VIRT,/proc/pid/status 看 VmRSS
  3. 检查 Swap 和缺页:vmstat -s 看 major fault 数量
  4. 怀疑泄漏时:用 valgrind 或 ASan 跑一遍
  5. 持续监控:用 watch -n 5 'free -h; vmstat -s | head -10' 观察趋势

一句话总结:内存问题不可怕,可怕的是没有数据就瞎猜。先用 free 和 vmstat 拿到第一手数据,再用 valgrind 精准定位。别上来就优化代码——先确认问题到底出在哪个层面。

想要更多资料,联系可可