代码审查是发现java内存泄漏最直接、成本最低的手段,需重点识别静态持有、生命周期错配、资源未释放三类强引用陷阱:盯死静态集合是否配套清理机制;检查threadlocal是否在finally中remove;确认所有资源是否用try-with-resources规范关闭;扫描内部类是否隐式持外层实例。

代码审查是发现 Java 内存泄漏最直接、成本最低的手段之一,不依赖运行时工具,却能提前拦截多数高发问题。关键不是“找 bug”,而是识别那些让对象无法被 GC 回收的强引用陷阱——尤其关注静态持有、生命周期错配、资源未释放这三类模式。
盯死静态集合和缓存类字段
静态变量生命周期与类加载器一致,一旦持有对象引用,除非显式清理或类卸载,否则对象永远不回收。
- 检查
public static List>、static Map, ?>、static Set>等声明,确认是否有对应的清除逻辑(如定时清理、容量限制、LRU 驱动的淘汰) - 警惕“注册-注销”不对称:监听器、回调、事件总线订阅者加入静态容器后,是否在反注册路径中调用
remove()? - 避免用
static final Map存储业务对象——它本质是单例级缓存,需配套驱逐策略,否则就是内存黑洞
排查 ThreadLocal 使用是否规范
ThreadLocal 是常见泄漏源,尤其在使用线程池(如 Tomcat 的 worker 线程)时:线程复用导致旧值滞留,且无法被 GC。
- 检查所有
ThreadLocal>声明,确认是否在业务逻辑结束前调用tl.remove()(不只是set(null)) - 避免在 ThreadLocal 中存储大对象(如
byte[]、完整 DTO),优先用局部变量或短期作用域对象 - 若必须长期持有,确保其值类型实现
AutoCloseable并配合 try-finally 或 try-with-resources 清理
核验资源类是否关闭,尤其在异常路径中
流、连接、通道等资源未关闭,不仅占用堆外内存,还可能因关联对象(如 BufferedInputStream 持有内部 byte[])导致堆内对象滞留。
- 所有
InputStream、OutputStream、Connection、Statement、ResultSet必须用try-with-resources,禁止裸写close()在 finally 块中(易漏或空指针) - 检查多层嵌套 try 或 if/else 分支:每个分支中打开的资源,是否都在对应出口前完成关闭?
- 留意自定义资源类:若实现了
AutoCloseable,确认 close() 方法真正释放了所有持有的引用(如清空内部缓存、断开监听)
扫描内部类与匿名类隐式引用
非静态内部类默认持外部类实例引用;匿名类同理。若该内部类被传递给长生命周期对象(如线程、静态监听器、异步回调),就会把整个外部类“钉”在内存里。
- 查看 Runnable、Comparator、Consumer 等函数式接口实现是否为非静态内部类或匿名类,且被注册到全局/静态作用域
- Android 场景中特别注意:Activity/Fragment 内部类作为 Handler、AsyncTask、RxJava Subscriber,极易引发 Context 泄漏
- 修复方式:改用静态内部类 + 显式弱引用(
WeakReference<activity></activity>),或提取为独立顶层类
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











