我的查询返回 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。