我的查询返回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占用了太多的记忆,结果可能存储在客户端捕获。但是,在我清理了一些对象(st.close()
和rs.close()
)之后,即使我手动调用垃圾收集器System.gc()
,循环后的空闲内存也不会增加(为什么?)。
我们假设我无法更改数据库设计,我需要所有查询结果。如何在处理后释放更多内存?
P.S。:我也尝试不使用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();
和rs = null;
//rs is Result instance
,但这似乎无济于事。为什么这两种方法之间的内存使用量有很大差异?在这两种方法中,我只传递了String。
答案 0 :(得分:0)
您应该缩小查询范围,尝试更具体,如果有必要,在查询中添加限制,您的java无法处理太大的结果
答案 1 :(得分:0)
如果您需要同时在内存中获取的所有数据(因此无法以块的形式处理它),那么您将需要有足够的内存。尝试使用1G内存。
忘记调用System.gc(),这不会有帮助(无论如何都会在抛出OutOfMemoryException之前调用它。)
我也注意到你没有关闭连接。您也应该这样做(如果您还没有连接池,请设置一个)。
当然,您可以使用分析器查看内存的实际位置。你现在正在做的是纯粹的猜测。
答案 2 :(得分:0)
我不认为很多人可能会遇到这个问题,但我仍然想发布我的解决方案以供参考。
在我的代码之前 查询是:
String sql = "SELECT column1, column2 ... FROM mytable";
和MyTableObj的setter是:
public void setColumn1(String columnStr) {
this._columnStr = columnStr == null ? "" : columnStr.trim();
}
更新后:
我更新的内容只是在查询中使用trim而不是使用java代码:
String sql = "SELECT TRIM(column1), TRIM(column2) ... FROM mytable";
public void setColumn1(String columnStr) {
this._columnStr = columnStr == null ? "" : columnStr;
}
使用这个udpated代码,它只需要大约 100 MB 内存,这比之前的内存使用量少得多( 380 MB 强>)。
我仍然无法给出一个有效的理由为什么java trim会消耗更多内存sql trim,如果有人知道原因,请帮我解释一下。我会非常感激。
经过多次测试,我发现它是数据。每行占用8 KB,31,000行占用大约240 MB内存。查询中的TRIM
只能用于那些短数据。
由于数据很大且内存有限,我现在只能限制查询结果。