内存泄漏读数来自数据库的+8百万条记录

时间:2014-03-22 12:12:50

标签: java mysql sql memory-leaks

我有一个包含+8百万条记录的数据库,我需要以特定方式处理这些记录,这些记录是用Java编写的。在查看了一些内容后,我发现了以下相关帖子:

这是我的代码,它返回存储在我的MySQL数据库的标签列中的项目:

public ResultSet getAllTags() {
    String query = "SELECT Tags FROM dataset";
    ResultSet rs = null;

    try {
        connection = ConnectionFactory.getConnection(DATABASE);
        preparedStatement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
        preparedStatement.setFetchSize(Integer.MIN_VALUE);
        rs = preparedStatement.executeQuery(query);
        // following line is for testing, to see what comes out of the resultset
        System.out.println("output: " + rs.getString(1));
        return rs;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    } finally {
        closeAll();
    }
}

这里我返回ResultSet,以便我处理rs.next()循环中的每一行。然而,在rs = preparedStatement.executeQuery(query);行,它开始吃掉我所有计算机的可用内存(我使用8GB内存在Mac OSX上工作。只有Eclipse打开我有+/- 5GB免费,当运行应用程序时它会下降直到<100MB免费)让我关闭数据库连接和应用程序等...所以我认为这可以称为内存泄漏?

有人可以解释我做错了什么以及为什么即使我按照其他记录数量相同的网页上的说明发生这个问题呢?

2 个答案:

答案 0 :(得分:4)

你唯一错误的就是使用一个愚蠢的数据库驱动程序(MySQL),它默认在内存中读取整个结果集。

尝试使用http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html中描述的useCursorFetch和defaultFetchSize属性来避免这种情况,并且您应该能够遍历这些行而无需获取内存中的所有内容(尽管未经过测试)。

注意该行

System.out.println("output: " + rs.getString(1));

将抛出异常,因为您尚未在结果集中调用next()。另请注意,如果closeAll()关闭连接,则调用者将无法遍历结果集,因为它将被关闭。您应该在关闭连接之前执行迭代。

请注意,驱动程序的the documentation说:

  

默认情况下,ResultSet完全检索并存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于MySQL网络协议的设计更容易实现。如果您正在使用具有大量行或大值的ResultSet,并且无法在JVM中为所需内存分配堆空间,则可以告诉驱动程序一次将结果流回一行。

     

要启用此功能,请按以下方式创建Statement实例:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
          java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

但您使用的是TYPE_SCROLL_SENSITIVE而不是TYPE_FORWARD_ONLY

答案 1 :(得分:1)

您是否考虑了获取大小的正值256/512/1024/2048。我希望为获取大小设置负值没有效果,但是这可能因驱动程序实现而异,您应该验证驱动程序文档中的实际行为。

    public void setFetchSize(int rows) throws SQLException {
    synchronized (checkClosed().getConnectionMutex()) {
        if (((rows < 0) && (rows != Integer.MIN_VALUE))
                || ((this.maxRows != 0) && (this.maxRows != -1) && (rows > this
                        .getMaxRows()))) {
            throw SQLError.createSQLException(
                    Messages.getString("Statement.7"), //$NON-NLS-1$
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$ //$NON-NLS-2$
        }

        this.fetchSize = rows;
    }
}