我的查詢以31,000個結果返回,每行有12列,每行包含大約8,000個字符(每行8KB)。下面是我如何處理:使用ResultSet從Oracle數據庫查詢時出現OutOfMemoryError
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()
,循環後的空閒內存也不會增加(爲什麼?)。
我們假設我無法更改數據庫設計,並且我需要所有查詢結果。如何在處理後釋放更多內存?
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傳遞。
其實你可能只是觀察它錯了。你認爲它使用了「太多」的內存,但它只是使用可用的內容,並沒有垃圾收集垃圾。你不會提到OutOfMemoryError,所以我假設你信任的是Runtime.freeMemory()值,只是**想着**你內存不足。 – Kayaman
實際上大多數內存不被String.trim()消耗,這是我觀察錯誤的部分。它應該是數據本身。如果每個數據是8 KB,那麼30,000個數據必須使用高達240 MB的內存。 – qkzhu
爲了得到正確的結果,應該使用profiler。不是Runtime.freeMemory()。 – Kayaman