首页/系统/Linux 运维命令速查/场景:判断内存是否泄漏(趋势法)
性能与监控48 条

场景:判断内存是否泄漏(趋势法)

怀疑泄漏别靠感觉:定时记录 RSS/available,按小时均值画趋势;排除缓存与周期性任务后再定性。

Linux 运维命令速查系统更新于 2026-10-13

这个场景解决什么

怀疑内存泄漏不能凭感觉,要靠趋势:按固定间隔记录进程的常驻内存与系统可用内存,把数据摆成时间序列,再看它是不是单调增长、增长是否跨过了业务周期。同时要排除掉缓存、周期性批量任务、以及 JVM 这类有 GC 行为的运行时带来的假信号。结论来自多条曲线的相互印证,而不是单点的数字。

基本信息

场景应用内存缓慢上涨,需要判断是不是泄漏
目标区分真正的泄漏、缓存增长与周期性压力
工具组合定时采样 /proc 数据、进程内存映射、运行时自身的堆信息
判断原则看趋势不看单点,采样周期要覆盖完整业务周期(含高峰)

典型使用场景

  • 上线后观察:新版本运行数日后内存持续走高,需要给出有无泄漏的结论
  • 容量规划:依据增长速率估算多久需要扩容或重启
  • 故障复盘:OOM 事件后还原内存曲线,判断是突增还是长期累积
  • 优化验证:改了缓存策略或对象生命周期后,对比前后趋势是否改善
  • 容器治理:结合控制组指标确认是进程增长还是整机压力

采样脚本

采样脚本
for i in $(seq 1 24); do
  pid=$(pgrep -f app.jar | head -1)
  rss=$(awk '/VmRSS/{print $2}' /proc/$pid/status 2>/dev/null)
  avail=$(awk '/MemAvailable/{print $2}' /proc/meminfo)
  printf '%s rss_kb=%s avail_kb=%s\n' "$(date -Is)" "${rss:-0}" "$avail"
  sleep 3600
done | tee /tmp/mem-trend-$(date +%F).log

分析与交叉验证

分析与交叉验证
awk '{print $2}' /tmp/mem-trend-*.log | sort -n | head        # 看最小值与量级
awk '{split($2,a,"="); print a[2]}' /tmp/mem-trend-*.log | head -n 24   # 抽成序列观察单调性
pmap -x $(pgrep -f app.jar | head -1) | tail -n 3             # 内存映射概览
# Java 应用还要看堆这条线
jcmd $(pgrep -f app.jar | head -1) GC.heap_info
jstat -gcutil $(pgrep -f app.jar | head -1) 60s 5

内存指标口径

指标含义误用风险
VSZ / VSS虚拟地址空间大小数值大是常态,不能作为占用判断
RSS常驻物理内存,含共享页多进程共享库会重复计数,直接相加会高估
PSS按共享比例分摊后的常驻内存更适合多进程场景的合计口径
USS该进程独占的常驻内存判断单个进程真实占用最准
堆内与堆外运行时管理的堆与堆外内存堆稳定而 RSS 上涨,说明问题在堆外
Java 应用要同时看两条线:堆用量(jstat 或 jcmd)与进程 RSS。堆稳定而 RSS 持续上涨,通常指向直接内存、线程栈或本地库;两条线同步上涨才更像堆内泄漏。
采样周期短看不出长尾泄漏:按周级别缓慢增长的问题需要跨业务周期采样,包含月末结算、批量任务这些高峰时段,否则容易得出「没有泄漏」的错误结论。

易错点

  • 把 RSS 直接与页缓存对比:口径不同,页缓存会被计入系统侧统计,混着看容易误判
  • 只看一天的数据就下结论:长尾泄漏按周增长,采样必须覆盖完整业务周期
  • 把 GC 后的阶梯下降当成泄漏消失:运行时会在压力后回收,趋势要看整体斜率而不是局部波动
  • 忽略容器与宿主的口径差异:控制组限制下的内存事件与整机指标要分开看

数据来源与参考

  • proc(5)(status 字段)
  • OpenJDK 诊断工具文档(jcmd/jstat)

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌