【问题标题】:Releasing JDBC resources释放 JDBC 资源
【发布时间】:2012-01-08 01:55:47
【问题描述】:

由于我使用 JDBC,我的应用程序出现了内存泄漏。我通过查看堆的可视转储并查看数千个 ResultSet 实例和相关对象来验证这一点。那么,我的问题是如何适当地管理 JDBC 使用的资源,以便对它们进行垃圾收集?我是否需要为使用的每个语句调用“.close()”?我是否需要对 ResultSet 本身调用“.close()”?

你将如何释放调用使用的内存:

ResultSet rs = connection.createStatement().executeQuery("some sql query");

??

我发现还有其他非常相似的问题。抱歉,如果这是多余的,但要么我不太了解答案,要么它们似乎并不普遍适用。我正在尝试就使用 JDBC 时如何管理内存给出权威答案。

::EDIT:: 添加一些代码示例

我有一个基本上是 JDBC 帮助器的类,我用它来简化数据库交互,主要的两种方法是执行插入或更新,以及执行选择语句。

这个用于执行插入或更新语句:

public int executeCommand(String sqlCommand) throws SQLException {
    if (connection == null || connection.isClosed()) {
        sqlConnect();
    }
    Statement st = connection.createStatement();
    int ret = st.executeUpdate(sqlCommand);
    st.close();
    return ret;
}

还有这个用于从选择中返回结果集:

public ResultSet executeSelect(String select) throws SQLException {
    if (connection == null || connection.isClosed()) {
        sqlConnect();
    }
    ResultSet rs = connection.createStatement().executeQuery(select);
    return rs;
}

使用executeSelect()方法后,我总是调用resultset.getStatement().close()

使用对象分配跟踪检查堆转储,显示语句仍被这两种方法保留...

【问题讨论】:

  • 确保关闭 try { } finally 块中的结果集和语句并将两者都设置为 null。使用这些的代码应该在块内
  • 如果您添加了您使用的典型 jdbc 执行代码,如果您没有释放任何 jdbc 资源,我们发表评论可能会更有帮助。

标签: java memory-management jdbc memory-leaks


【解决方案1】:

您应该在完成后关闭 JDBC 语句。当关联的语句关闭时,结果集应该被释放——但如果你愿意,你可以明确地这样做。

您需要确保在异常情况下也关闭所有 JDBC 资源。

使用 Try-Catch-Finally 块 - 例如:

try {
    conn = dataSource.getConnection();
    stmt = conn.createStatement();
    rs = stmet.executeQuery("select * from sometable");
    stmt.close();
    conn.close();
} catch (Throwable t) {
    // do error handling
} finally {
    try {
         if (stmt != null) { 
             stmt.close();
         }
         if (conn != null) { 
             conn.close();
         }
    } catch(Exception e) {

    }
}

【讨论】:

  • 这段代码有问题...如果 stmt.close() 抛出 SQLException,那么 conn.close() 将不会被调用。您需要用单独的 try/catch 块包装 stmt.close() 和 conn.close() 。更有理由将所有这些样板代码抽象出来......正如在其他地方提到的,Spring 的 JdbcTemplate 是一个不错的选择。
【解决方案2】:

我从我一直在做的一个项目中复制了这个。我正在重构它以使用 Hibernate(从代码中应该清楚为什么!!)。使用像 Hibernate 这样的 ORM 工具是解决问题的一种方法。否则,这是我使用普通 DAO 访问数据的方式。我们的代码中没有内存泄漏,因此作为模板可能会有所帮助。希望对您有所帮助,内存泄漏太可怕了!

@Override
public List<CampaignsDTO> getCampaign(String key) {
    ResultSet resultSet = null;
    PreparedStatement statement = null;
    try {
        statement = connection.prepareStatement(getSQL("CampaignsDAOImpl.getPendingCampaigns"));
        statement.setString(1, key);
        resultSet = statement.executeQuery();

        List<CampaignsDTO> list = new ArrayList<CampaignsDTO>();

        while (resultSet.next()) {
            list.add(new CampaignsDTO(
                    resultSet.getTimestamp(resultSet.findColumn("cmp_name")), 
                    ...));
        }
        return list;
    } catch (SQLException e) {
        logger.fatal(LoggerCodes.DATABASE_ERROR, e);
        throw new RuntimeException(e);
    } finally {
        close(statement);
    }
} 

close() 方法如下所示:

public void close(PreparedStatement statement) {
    try {
        if (statement != null && !statement.isClosed())
            statement.close();
    } catch (SQLException e) {
        logger.debug(LoggerCodes.TRACE, "Warning! PreparedStatement could not be closed.");
    }
}

【讨论】:

    【解决方案3】:

    是的,结果集和语句应始终在 finally 块中关闭。使用诸如 Spring 的 JdbcTemplate 之类的 JDBC 包装器有助于减少代码的冗长并为您关闭所有内容。

    【讨论】:

      【解决方案4】:

      如果你不打算重复使用它,你应该关闭它。首先关闭 ResultSet 通常是一种很好的形式,因为某些实现不会自动关闭 ResultSet(即使它们应该关闭)。

      如果您重复相同的查询,您可能应该使用 PreparedStatement 来减少解析开销。如果你在查询中添加参数,你真的应该使用 PreparedStatement 来避免 sql 注入的风险。

      【讨论】:

        猜你喜欢
        • 2015-10-04
        • 2012-05-04
        • 2018-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多