定位长久存活对象需先用jprofiler标记堆并对比快照筛选跨gc周期存活对象,再结合jmap+mat分析强引用链,最后通过代码验证静态集合、内部类、资源未关闭及threadlocal等典型泄漏模式。

定位长久存活的对象引用,核心是识别那些本该被回收却持续驻留堆中的对象——它们往往被意外的强引用链牢牢“钉住”。JProfiler 的 标记堆(Mark Heap) 是最直接、低开销且生产友好的方式;配合 jmap + MAT 则适合深度回溯引用路径。
用 JProfiler 快速聚焦长期驻留对象
它不依赖 JVM 暴露的“幸存代数”,而是通过两次快照对比,精准筛选出跨 GC 周期仍存活的对象:
- 在 Heap Walker 中点击 “Mark heap”(或菜单 Profiling → Mark Heap),为当前所有存活对象打上 “old” 标签;
- 让应用运行并触发至少一次 Full GC(可在 Live Memory → GC Events 中确认);
- 再次获取堆快照,进入 All Objects 视图,启用 “Show only old objects” 过滤;
- 按 “Objects” 数量降序排列,重点关注实例数随时间稳定增长的类,比如
ArrayList、HashMap、自定义监听器或缓存实体。
用 jmap + MAT 追查强引用链
当需要确认“为什么这些对象没被回收”,就得看谁在持有它们:
Java Linux版下载入口,提供 Oracle JDK 26.0.2 官方 Linux 安装包、Java 环境配置、JDBC 数据库连接和 Java 服务端开发相关信息。
- 用
jps找到目标进程 PID,执行jmap -dump:format=b,file=heap.hprof <pid></pid>导出堆转储; - 用 Eclipse MAT 打开 .hprof 文件,先看 Leak Suspects Report,它会自动标出最可能的泄漏点;
- 若报告不明确,打开 Dominator Tree,找保留内存最多的对象,右键选择 Path to GC Roots → with all references;
- 重点检查路径中是否出现静态字段(如
MyService.CACHE)、未注销的监听器、ThreadLocal 或内部类隐式引用等典型泄漏模式。
结合代码与场景验证泄漏合理性
工具只给出线索,最终要靠人工判断引用是否“合理”:
- 静态集合类(如
private static List<listener> listeners = new ArrayList();</listener>)持续 add 却无 remove,就是高危信号; - 非静态内部类作为回调注册到全局事件总线,而外部 Activity 或 Service 已销毁,但内部类实例仍在,就会拖住整个外部对象;
- 数据库连接、文件流、Socket 等资源未在 finally 或 try-with-resources 中关闭,底层对象可能因关联引用无法释放;
- ThreadLocal 在线程池场景下未调用
remove(),会导致变量随线程复用而累积。
辅助观察:GC 行为与内存趋势
光看对象不够,还要看系统反应:
- 用 VisualVM 或 JProfiler 的 Live Memory 视图,观察老年代(Old Gen)使用率是否呈阶梯式上升;
- 执行 Full GC 后,老年代内存是否明显回落?若基本不降,说明有大量本该回收的对象被强引用滞留;
- 搭配
jstat -gc <pid></pid>查看 YGC、FGC 频次和耗时,频繁 Full GC 且回收效果差,是泄漏的典型外在表现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










