You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JVM无法对未关闭的连接及资源进行垃圾回收?

JVM无法自动回收未关闭资源的核心原因

JVM垃圾回收(GC)的作用边界非常明确:仅回收JVM堆内存中无任何强引用关联的Java对象,完全不干涉操作系统内核层面分配的资源。未关闭的连接/资源无法被自动回收,本质是两个层面的问题:

  • 未关闭资源对应的Java包装对象,通常被静态集合、长生命周期容器、连接池等对象持有强引用,GC不会判定为可回收对象
  • 即使Java对象被回收,操作系统层面分配的文件句柄、TCP会话、数据库会话、本地内存等内核资源,也不会被GC自动释放,必须调用对应close()方法主动通知内核回收

示例1:未关闭的文件流泄漏

public class FileLeakDemo {
    // 长生命周期的静态集合,默认持有所有打开的流对象强引用
    private static final List<InputStream> OPEN_STREAMS = new ArrayList<>();

    public void readFile(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        OPEN_STREAMS.add(fis);
        // 仅读取内容,未移除集合引用,也未调用fis.close()
        byte[] buffer = new byte[1024];
        fis.read(buffer);
    }
}

上述代码中,FileInputStream对象被静态集合持有强引用,程序运行全程不会被GC回收。同时每个FileInputStream对应操作系统分配的1个文件句柄,就算手动把对象从集合移除触发GC回收,只要没有调用close(),该文件句柄依然会被操作系统标记为占用,直到进程退出才会释放,累积到系统上限就会抛出Too many open files错误。


示例2:未关闭的数据库连接泄漏

public class DbConnectionLeakDemo {
    public void queryUser() throws SQLException {
        // 从连接池获取数据库连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("select * from user");
        // 处理完结果直接返回,未关闭rs、stmt、conn
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
    }
}

这里的Connection对象底层对应数据库服务端的1个会话连接,以及操作系统层面的TCP连接。未调用close()的情况下,连接对象会被连接池内部持有强引用,GC不会回收,数据库服务端的会话也会一直保持活跃,占满连接池上限后新的请求就无法获取连接,抛出连接耗尽异常。


兜底机制说明

部分JDK自带的资源类(如FileInputStream、ByteBuffer)实现了Cleaner虚引用机制,当Java对象被GC回收时,会触发兜底逻辑释放关联的内核资源。但该机制完全依赖GC的触发时机,不可控性极高,如果资源分配速度远快于GC触发频率,依然会出现资源耗尽问题,绝对不能替代手动close()操作。

内容的提问来源于stack exchange,提问作者Sachin Rane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 23:24:03