java内存泄漏排查需先用free和top排除系统级内存问题:available持续低于10%或swap被使用才属真紧张;若available充足而java进程rss持续上涨,则进入jvm堆内分析。

Java 内存泄漏排查不能只盯着 JVM 堆,得先确认系统层是否真缺内存。top 和 free 是第一道筛子,它们不直接告诉你“哪个对象没释放”,但能快速排除干扰、锁定问题方向。
看 free:先分清“真紧张”和“假告急”
执行 free -h,重点盯三列:
- available:真正能立刻分配给新进程的内存。如果它持续低于总内存的 10%(比如 16G 机器长期低于 1.6G),才说明系统级内存吃紧;
- buff/cache:这部分高不等于出问题,反而是 Linux 在积极缓存文件提升 IO 性能。数据库、日志服务这类读写密集型 Java 应用,buff/cache 占 5–8G 很常见;
- swap 下的 used:只要 swap 开始被使用(si/so 持续非零),就说明物理内存已严重不足,JVM 可能因卡顿触发 GC 失败或 OOM Killer 干预。
如果 available 充足、swap 零使用,那即使 top 里 Java 进程 RSS 达到 4G,也大概率不是系统内存问题,而是 JVM 自身堆内对象堆积——该进 JVM 工具环节了。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
用 top 定位可疑 Java 进程
运行 top 后按大写 M,按内存占用降序排列:
- 找 %MEM 异常高(比如单个进程占 30%+)且 RES(实际物理内存)随时间持续上涨的 Java 进程;
- 注意区分 VIRT 和 RES:VIRT 通常虚高(含未分配的虚拟地址空间),RES 才反映真实驻留内存;
- 如果 RES 稳定在某个值上下小幅波动,基本排除泄漏;若每小时涨 200MB 且不回落,尤其伴随 GC 频率上升,就是强信号。
交叉验证:free + top + 进程 PID 联动
当发现某个 Java 进程 RES 异常时,别急着 dump,先做两件事:
- 记下它的 PID,执行 ps -o pid,rss,%mem,vsz,comm -p [PID],确认 RSS 是否与 top 一致,排除显示延迟或采样误差;
- 同时开一个终端跑 watch -n 5 'free -h',观察 available 是否同步下降。如果 available 不变而 RES 上涨,说明 JVM 在用堆外内存(如 DirectByteBuffer、JNI、Metaspace 膨胀)或共享内存(/dev/shm);
- 如果 available 和 RES 同步跌,再查 cat /proc/[PID]/status | grep -E "VmRSS|VmSize|HugetlbPages",确认是否堆内增长主导,还是 mmap 区域异常扩张。
警惕三个“看起来像泄漏”的假象
有些情况会让 top/free 显示“内存越来越高”,但并非 Java 代码泄漏:
- Page Cache 被 Java 进程间接占用:比如应用大量读取大文件,内核把内容缓存在 buff/cache,这部分会计入系统 used,但不属于该进程 RSS;
- tmpfs 占用:检查 df -hT | grep tmpfs,尤其是 /dev/shm。某些框架(如 Spark、Flink)或数据库驱动会往这里写临时数据,这部分内存算在 MemUsed 里,却不会出现在任何进程 RSS 中;
- JVM Native Memory Tracking(NMT)未开启时的堆外泄漏:DirectByteBuffer、Compressed Class Space、线程栈等不走 GC,但会吃物理内存,top 看得到 RSS 上涨,free 看得到 available 下降,却查不到堆内对象异常。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










