请列举 Java 应用中发生内存泄漏的常见典型场景,并解释其成因。
考察说明
考察候选人对 Java 内存管理机制和常见内存泄漏模式的理解程度。
回答思路
- 【回答框架 1】内存泄漏的本质是对象不再被业务使用,但依然被 GC Roots 强引用,导致无法被回收。在 Java 中,常见场景包括静态集合类持有对象、未关闭的资源、不恰当的缓存设计以及监听器或回调未解绑。
- 【回答框架 2】静态集合如 HashMap 或 List 持有对象,若未及时清理,会导致集合容量膨胀且对象无法回收。例如缓存中 key 或 value 长期不失效,就造成内存占用持续增长。
- 【回答框架 3】数据库连接、IO 流、网络连接等资源未在 finally 或 try-with-resources 中关闭,会占用堆外内存或持有内部缓冲区,形成类似内存泄漏的效果。
- 【回答框架 4】监听器模式中,对象注册了事件监听但未在销毁时移除,导致监听器被长时间引用;同理,单例对象持有非单例的上下文或活动引用,也会造成泄漏。
- 【回答框架 5】ThreadLocal 使用不当也会泄漏:若线程池中线程存活时间长,而 ThreadLocal 中的值未被及时 remove,则这些值会一直绑定在线程上,无法被回收。
- 【关键点 1】内存泄漏的根本原因多为长生命周期对象持有短生命周期对象的引用。
- 【关键点 2】静态集合、未关闭资源、监听器未移除是常见泄漏场景。
- 【关键点 3】ThreadLocal 在配合线程池时需显式 remove,否则可能泄漏。
- 【关键点 4】通过 heap dump 和 MAT 等工具分析强引用路径可定位泄漏点。
- 【易错点 1】误将内存溢出等同于内存泄漏,溢出可能由大对象导致,泄漏是累积效应。
- 【易错点 2】只关注堆内泄漏而忽视堆外内存(如直接缓冲区)未释放的问题。
- 【易错点 3】认为所有缓存都会泄漏,实际需结合回收策略和大小限制判断。