Statment 与 ResultSet 导致内存泄漏

背景

最近看 MySQL 官方驱动程序 MySQL Connector/J 的文档,发现一处比较奇怪的代码。

Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select id,name from tempdb.t;");

// ... 

// 
rs.close();
stmt.close();

之前的主力语言是 Python3 ,在那里我们只对 Connection 对象做资源的回收,看 java 代码他还要对 Statement ResultSet 类型的对象做资源的回收。难不成不回收的话还真会有内存泄漏?


第一版 不回收--泄漏内存

public class ResourceTest {
    /**
     * 测试不 close 语句对象和结果集对象会不会有内存泄漏的问题
     */
    public static void Qury(Connection conn) {
        try {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("select id,name from tempdb.t;");
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                System.out.println("line [id=" + id + " name=" + name + "]");
                // rs.close();
                // stmt.close();
            }
        } catch (SQLException ex) {

        }
    }

    public static void main(String[] args) {
        System.out.println("test starting ");
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/tempdb?user=appuser");
            while(true) {
                Qury(conn);
            }
        } catch (SQLException ex) {

        } catch (Exception ex) {

        }
        System.out.println("test ending ");
    }
}

这版本下我的机器内存使用量从 12.02G 不一下就上涨到了 14G 看样子还在一直涨 ;应该是内存泄漏了。


第二版 回收--不泄漏内存

public class ResourceTest {
    /**
     * 测试不 close 语句对象和结果集对象会不会有内存泄漏的问题
     */
    public static void Qury(Connection conn) {
        try {
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("select id,name from tempdb.t;");
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                System.out.println("line [id=" + id + " name=" + name + "]");
                // close 进行内存回收
                rs.close();
                stmt.close();
            }
        } catch (SQLException ex) {

        }
    }

    public static void main(String[] args) {
        System.out.println("test starting ");
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/tempdb?user=appuser");
            while(true) {
                Qury(conn);
            }
        } catch (SQLException ex) {

        } catch (Exception ex) {

        }
        System.out.println("test ending ");
    }
}

这个本版本的代码内存使用量基本不变。