java内存泄漏本质是对象不再使用却被隐式引用导致gc无法回收,需排查静态集合、内部类监听器、未关闭资源及用mat分析gc roots路径。

Java内存泄漏的本质,不是“没释放内存”,而是“对象明明不用了,却还被某个地方悄悄拽着”,导致垃圾回收器(GC)无法清理。识别和清理这类引用,关键在于找到谁在持有、为什么没松手、以及怎么安全放手。
盯住静态集合里的“老住户”
静态变量生命周期贯穿整个JVM,它持有的集合(如static Map、static List)就像个永不关门的仓库。往里塞的对象,只要没主动清空,就一直赖着不走。
- 检查所有static修饰的集合类,确认是否有长期添加但从未移除的操作
- 如果必须用静态缓存,加上清理机制:比如按时间淘汰(LRU)、按使用频次清理,或提供显式的clear()入口
- 避免把短命对象(如请求上下文、临时DTO)直接放进静态集合;优先考虑WeakHashMap或SoftReference包装
留意内部类和监听器的“隐形牵连”
非静态内部类默认持有外部类实例的强引用。如果这个内部类被注册成监听器、提交到线程池,或者作为回调长期存活,外部类哪怕业务逻辑已结束,也会被一起钉在内存里。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 若内部类不需要访问外部类成员,声明为static,切断隐式引用
- 监听器注册后,务必在对应时机(如Activity销毁、Fragment detach、服务关闭)调用removeXXXListener()
- 使用弱引用监听器模式:自定义WeakReference
容器,在触发前先判空
关掉没用完的资源“水龙头”
流、连接、通道这些资源本身可能不大,但它们背后常关联着本地句柄(file descriptor、socket等),而JVM又通过对象维持着对这些句柄的管理。不关,不仅内存难回收,还可能耗尽系统级资源。
- 所有实现了AutoCloseable的资源,一律用try-with-resources语法
- 数据库连接、HTTP客户端连接池要配置合理的最大空闲数和超时时间,避免连接堆积
- 手动关闭场景下,finally块中执行close(),并做好null判断和异常吞并
用工具定位“拽得最紧”的那个引用
靠猜效率低,靠日志太滞后。真正卡点在于看清对象到GC Roots的完整路径——谁在根上拽着它?
- 运行时用jstat -gc
观察老年代增长趋势;持续上升且Full GC无效,基本可定性泄漏 - 触发一次堆转储:jmap -dump:format=b,file=heap.hprof
- 用MAT打开.hprof文件,优先看Leak Suspects报告,再深入Path to GC Roots(排除weak/soft/phantom reference路径)
- 重点关注“Shallow Heap”小但“Retained Heap”巨大的对象,它们往往是泄漏源头的“中转站”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










