Java内存泄漏主因是对象被意外强引用,需从设计规避:静态集合应避免长期持有、内部类须static或弱引用、ThreadLocal用后remove、资源必须try-with-resources关闭。

Java 应用中因引用不当导致的内存泄漏,核心在于“对象本该被回收,却被意外持有了强引用”。这类问题不依赖手动释放内存,而是要切断那些不该存在的引用链。关键不是等它爆 OOM,而是从设计和编码习惯上提前规避。
静态集合类长期持有对象
静态变量生命周期与 JVM 一致,一旦往 static List/Map 里塞对象却不清理,这些对象就永远无法回收。
- 避免直接用 public static ArrayList 存业务数据;改用局部变量或有明确生命周期管理的缓存(如 Caffeine)
- 若必须用静态集合,配合定时清理或过期机制,比如用 ScheduledExecutorService 每分钟清空超时项
- 对缓存场景,优先选 WeakHashMap(键为弱引用)或 ConcurrentLinkedQueue + 弱引用包装,让 GC 可自然介入
内部类隐式持有外部类引用
非静态内部类、匿名类(如 new Thread()、new OnClickListener())会自动持有外部类 this 引用。若该内部类被注册到全局监听器、线程池或静态容器中,外部类就会被“拖住”无法回收。
- 把内部类声明为 static,再显式传入所需字段,切断隐式引用
- 使用 WeakReference 包装外部类实例,在内部类中通过 get() 安全访问
- 注册监听器后,务必在对应生命周期结束时调用 removeXXXListener,尤其 Activity/Fragment 销毁时
ThreadLocal 未及时清理
ThreadLocal 变量在线程内独享,但线程复用(如线程池)时,若未调用 remove(),旧值会一直留在 ThreadLocalMap 中,形成“线程级内存泄漏”。
立即学习“Java免费学习笔记(深入)”;
- 每次使用完 ThreadLocal 后,显式调用 .remove(),不要只靠 set(null)
- 在 try-finally 块中确保 remove 执行,哪怕发生异常也不遗漏
- 避免将大对象(如 Map、List)存入 ThreadLocal,尤其不能存上下文或 DAO 实例
资源未关闭引发间接泄漏
InputStream、Connection、Socket 等资源本身可能不大,但其底层 native 句柄未释放,会阻止关联对象(如 Buffer、Statement)被回收,最终拖垮堆内存。
- 强制使用 try-with-resources,JDK 7+ 语法能保证 close() 在异常或正常路径下都执行
- 若需手动 close,必须写在 finally 块中,并判空防止 NullPointerException
- 自定义资源类实现 AutoCloseable 接口,方便统一纳入资源管理流程


















