MST

星途 面试题库

面试题:Java内存泄漏场景及排查方法

列举至少三种常见的Java内存泄漏场景,并详细说明如何利用工具(如Jconsole、VisualVM等)排查和定位内存泄漏问题。针对每种场景,解释内存泄漏产生的根本原因以及如何避免。
48.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

常见Java内存泄漏场景

  1. 静态集合类导致的内存泄漏
    • 根本原因:静态集合类(如HashMapVector等)生命周期与应用程序相同。如果在集合中放入对象后,未及时清理,即使这些对象在业务逻辑中不再使用,由于集合对其存在引用,垃圾回收器无法回收这些对象,从而导致内存泄漏。
    • 避免方法:在对象不再使用时,及时从静态集合中移除。例如使用map.remove(key)方法移除相应的键值对。
  2. 监听器和回调未注销导致的内存泄漏
    • 根本原因:当注册监听器或回调时,如果在不再需要它们时没有注销,被监听的对象会一直持有对监听器或回调对象的引用,即使监听器或回调对象在其他地方已不再使用,垃圾回收器也不能回收它们,造成内存泄漏。
    • 避免方法:在对象不再使用时,调用注销方法,例如removeListener()方法。
  3. 数据库连接未关闭导致的内存泄漏
    • 根本原因:数据库连接(Connection)对象在使用完毕后,如果没有显式关闭,连接对象会一直占用资源,并且相关的资源(如Statement、ResultSet等)也不会被释放,造成内存泄漏。
    • 避免方法:在使用完数据库连接相关对象后,在finally块中关闭连接、Statement和ResultSet。例如:
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
    conn = DriverManager.getConnection(url, username, password);
    stmt = conn.createStatement();
    rs = stmt.executeQuery(sql);
    // 处理结果集
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        if (rs != null) rs.close();
        if (stmt != null) stmt.close();
        if (conn != null) conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  1. 内部类持有外部类引用导致的内存泄漏
    • 根本原因:非静态内部类会隐式持有外部类的引用,如果内部类对象的生命周期比外部类对象长,外部类对象在应该被回收时,由于内部类的引用而无法被回收,导致内存泄漏。
    • 避免方法:将内部类声明为静态内部类,如果需要访问外部类的成员,可以通过弱引用(WeakReference)来实现。

利用工具排查和定位内存泄漏问题

  1. Jconsole
    • 排查步骤
      • 启动Jconsole,并连接到目标Java进程。
      • 在“内存”标签页中,观察堆内存使用情况的变化趋势。如果堆内存持续增长,且在执行垃圾回收后没有明显下降,可能存在内存泄漏。
      • 点击“执行GC”按钮手动触发垃圾回收,进一步确认内存是否能被回收。
      • 在“线程”标签页中,查看线程状态,检查是否有长时间运行且不释放资源的线程,某些内存泄漏可能与线程异常有关。
      • 使用“MBean”中的com.sun.management:type=Memory MBean获取详细的内存信息,如各代堆内存的使用情况等,辅助分析。
    • 定位方法:如果怀疑存在内存泄漏,可以使用Jconsole的“堆内存”直方图功能。该功能可以列出堆中对象的数量和大小,通过观察对象数量和大小的变化,找出可能导致内存泄漏的对象类型。然后可以结合代码,分析这些对象是如何产生和引用的,从而定位内存泄漏点。
  2. VisualVM
    • 排查步骤
      • 启动VisualVM,并连接到目标Java进程。
      • 在“概述”标签页中,可以查看基本的JVM信息和进程状态。
      • 在“监视”标签页中,实时监控CPU、内存、类和线程的使用情况。关注内存使用曲线,如果内存持续上升且垃圾回收后不下降,可能存在内存泄漏。
      • 点击“垃圾回收”按钮手动触发垃圾回收,观察内存变化。
    • 定位方法
      • 使用“抽样器”标签页中的“内存”抽样功能。点击“开始”进行内存抽样,抽样完成后,会展示堆中对象的分布情况,包括对象的数量、大小和类名。通过分析对象数量和大小的增长趋势,找出可疑的对象类型。
      • 对于可疑的对象类型,可以查看其引用树。在VisualVM中右键点击对象类型,选择“显示对象” -> “实例”,然后右键点击实例,选择“显示引用树”。通过引用树可以查看对象之间的引用关系,从而定位到内存泄漏的源头,即哪些对象对这些可疑对象存在不必要的强引用。