0

我的查询返回 31,000 个结果,每行 12 列,每行包含大约 8,000 个字符(每行 8KB)。这是我的处理方式:

public List<MyTableObj> getRecords(Connection con) {
    List<MyTableObj> list = new ArrayList<MyTableObj>();
    String sql = "my query..."; 

    ResultSet rs = null;
    Statement st = null;

    con.setAutoCommit(false);
    st = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
    st.setFetchSize(50);
    rs = st.executeQuery(sql);

    try {
        System.out.println("Before MemoryFreeSize = " + (double)Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");

        while ( rs.next() ) {
            MyTableObjitem item = new MyTableObj();
            item.setColumn1( rs.getString("column1") );
            ... ...
            item.setColumn12( rs.getString("column12") );

            list.add( item );
        } // end loop

        // try to release some memory, but it's not working at all
        if ( st != null ) st.close();
        if ( rs != null ) rs.close();
        st = null; rs = null;
    }
    catch ( Exception e ) { //do something }
    System.out.println("After MemoryFreeSize = " + (double)Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");

    return list;
} // end getRecords

如果每行占用 8kb 内存,则 31k 应该占用 242mb 内存。在完成循环查询结果后,我的剩余内存只有 142mb,这不足以完成我的其他进程。

我搜索了许多解决方案,并尝试将堆内存设置为 512mb -Xmx512m -Xms512m,并且还设置了获取大小setFetchSize(50)

我怀疑是ResultSet占用了太多内存,结果可能存储在客户端catch中。但是,在我清除了一些对象(st.close()rs.close())之后,即使我手动调用了垃圾收集器System.gc(),循环后的空闲内存也不会增加(为什么?)。

假设我无法更改数据库设计,并且我需要所有查询结果。处理后如何释放更多内存?

PS:我也尝试不使用ResultSet.getString()硬编码字符串并将其替换,循环后,我得到了 450mb 的可用内存。

我发现,如果我这样做:

// + counter to make the value different for each row, for testing purpose
item.setColumn1( "Constant String from Column1" + counter );
... ...
item.setColumn12( "Constant String from Column12" + counter );
counter++;

它只使用了大约 60MB 的内存。但如果我这样做:

item.setColumn1( rs.getString("column1") );
... ...
item.setColumn12( rs.getString("column12") );

它使用了高达 380MB 的内存。

我已经做过rs.close();and rs = null; //rs is Result instance,但这似乎无济于事。为什么这两种方法之间的内存使用差异如此之大?在这两种方法中,我只传入了 String。

4

3 回答 3

0

如果您需要同时在内存中获取的所有数据(因此您不能分块处理它),那么您需要有足够的内存来存储它。用1G内存试试。

忘记调用 System.gc(),这无济于事(无论如何都会在抛出 OutOfMemoryException 之前调用它)。

我还注意到您没有关闭连接。您可能也应该这样做(如果您还没有连接池,请设置一个)。

当然,您可以使用分析器来查看内存的实际去向。你现在所做的纯粹是猜测。

于 2013-10-03T08:08:47.563 回答
0

您应该缩小查询范围,尝试更具体,如有必要,在您的查询中添加限制,您的 java 无法处理太大的结果

于 2013-10-03T07:46:17.940 回答
0

我不认为很多人可能会遇到这个问题,但我仍然想发布我的解决方案以供参考。

在我的代码之前, 查询是:

String sql = "SELECT column1, column2 ... FROM mytable";

MyTableObj 的设置器是:

public void setColumn1(String columnStr) {
    this._columnStr = columnStr == null ? "" : columnStr.trim();
}

更新后:

我更新的只是在查询中使用修剪而不是使用 java 代码:

String sql = "SELECT TRIM(column1), TRIM(column2) ... FROM mytable";

public void setColumn1(String columnStr) {
    this._columnStr = columnStr == null ? "" : columnStr;
}

使用这个更新的代码,它只需要大约100 MB内存,这比以前的内存使用量(380 MB)要少得多。

我仍然无法给出java trim消耗更多内存sql trim的正当理由,如果有人知道原因,请帮我解释一下。我会很感激的。

经过多次测试,我发现它是数据。每行占用 8 KB,31,000 行占用大约 240MB 内存。TRIM在查询中只能适用于那些短数据。

由于数据量大,内存有限,目前只能对查询结果进行限制。

于 2013-10-04T04:08:25.680 回答