面试题答案
一键面试常见内存泄漏场景
- 静态集合类导致的内存泄漏:像
HashMap
、ArrayList
等静态集合,若持续添加元素且不清理,元素无法被垃圾回收,因为静态变量生命周期和应用程序相同。例如,在一个类中定义private static List<Object> list = new ArrayList<>();
,不断往list
添加对象但不做清理操作。 - 监听器和回调未释放:注册监听器后,如果没有在合适时机注销,监听器会持有被监听对象的引用,导致对象无法被回收。比如,
addXXXListener
方法注册监听器后,没有对应的removeXXXListener
方法调用。 - 数据库连接、文件句柄等资源未关闭:数据库连接用完后若不关闭,相关资源一直被占用,不会被垃圾回收,导致内存泄漏。同样,文件操作完毕未关闭文件句柄也会有此问题。
- 内部类持有外部类引用:非静态内部类会隐式持有外部类的引用,如果内部类对象生命周期较长,外部类对象无法被回收。例如,在外部类中定义一个非静态内部类,并在外部类方法中创建内部类对象且长时间存活。
排查内存泄漏问题方法
- 使用内存分析工具:
- MAT(Eclipse Memory Analyzer):它可以生成堆转储快照(Heap Dump),通过分析快照找出内存占用大的对象、对象之间的引用关系等。导入Heap Dump文件后,利用
Dominator Tree
视图查看哪些对象占用大量内存,通过Path To GC Roots
查看对象到GC Roots的引用路径,判断是否存在不合理引用导致对象无法回收。 - VisualVM:是JDK自带的性能分析工具,能连接到运行中的Java进程,获取堆内存使用情况、线程信息等。通过它生成堆快照,分析内存中的对象分布,查找可疑的大对象和长时间存活对象。
- MAT(Eclipse Memory Analyzer):它可以生成堆转储快照(Heap Dump),通过分析快照找出内存占用大的对象、对象之间的引用关系等。导入Heap Dump文件后,利用
- 代码审查:检查代码中对象的创建和使用逻辑,查看是否存在对象创建后未释放,尤其是静态变量、监听器、资源连接等。例如,检查是否有未关闭的数据库连接代码块,查看注册监听器的地方是否有对应的注销逻辑。
- 日志分析:在关键代码位置添加日志,记录对象的生命周期信息,如对象的创建、使用和销毁时间。通过分析日志,判断对象是否在预期时间内被释放,找出可能存在内存泄漏的代码段。