2011-08-04 130 views
4

我正在使用MySQL和Java來選擇約50000條記錄。 奇怪的是,當我使用ResultSet和next()方法來讀取數據,我看到,在取我的Java應用程序增加RAM的使用。它以255 MB開始並增加到379 MB! 我使用的代碼是在這裏:mysql使用ResultSet時內存(RAM)使用率增加了嗎?

try { 
    Class.forName("com.mysql.jdbc.Driver"); 
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/#mysql50#crawler - used in report?" + "user=root&password=&useUnicode=true&characterEncoding=UTF-8"); 
    Statement st = conn.createStatement(); 
    ResultSet rsDBReader = st.executeQuery("SELECT Id, Content FROM DocsArchive"); 
    while (rsDBReader.next()) { 
     int docId = rsDBReader.getInt(1); 
     String content = rsDBReader.getString(2); 
     . . . 
     } 
    rsDBReader.close(); 
    st.close(); 
    conn.close(); 
} catch (Exception e) { 
    System.out.println("Exception in reading data: " + e); 
} 

我相信,內存使用量是ResultSet中,而不是程序的其它部分。 在這個程序中,我不需要更新記錄,所以我想在完成工作後刪除每條記錄。 我的猜測是,已經讀過的記錄不會被刪除,程序也不會釋放他們的記憶。所以我用了一些技巧來避免這種情況,比如使用下面的代碼:

Statement st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT); 

st.setFetchSize(500); 
rsDBReader.setFetchSize(500); 

但是他們沒有改變任何東西。 :(

所以我需要一些方法,消除行(發行版)的內存已讀取。

另一個有趣的一點是,即使在完成功能和關閉的ResultSet,語句和連接,並且要經過該計劃的另一部分,仍然是程序存儲器的使用不會降低! 謝謝

+2

MySQL高速緩存查詢結果。 – Johan

+0

好的謝謝,但是我怎樣才能清空咖啡? – Soheil

+0

你可以做什麼,以節省內存(更多的時間雖然)是限制每個SELECT語句 – RMT

回答

2

我建議你限制在查詢中檢索行的數量。50000是很多,爲什麼不能有一個循環其獲取,讓我們說,1000行每一次?

你可以做到這一點使用limit語句,描述here。對於您正在處理的數據量來說,最好務實。你目前的選擇今天可能會返回50000行,但如果明天增長到100萬呢?你的應用程序會窒息。所以,一步一步做你的處理。

6

使用Statement.setFetchSize()向驅動程序提供一個提示,告知驅動程序它應該爲包含一定行數的數據流傳輸ResultSet。據我所知,MySQL連接-J驅動程序不明白的提示和溪流ResultSet S(但這是在MySQL中的情況下,時間限制爲行)。

默認值爲0,將確保連接器-J驅動程序將獲取完整ResultSet沒有進行流式傳輸。這就是爲什麼你需要提供一個明確的值--MySQL的Integer.MIN_VALUE。

聲明:

Statement st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT); 

不會導致流的ResultSet(至少不是在它自己的協議)。它僅僅確保了結果集不爲「滾動」(即只能在向前的方向上遍歷),而不是「可更新的」,並且當事務提交底層光標將被關閉。

JDBC implementation notes of MySQL所述,必須調用以上語句(不包括ResultSet.CLOSE_CURSORS_AT_COMMIT參數)並結合Statement.setFetchSize(Integer.MIN_VALUE)調用來逐個發生流式傳輸。有關這種情況的相關警告也被記錄在案。

請注意,遊標的可保存性未在MySQL文檔中提到的示例中指定。如果您需要的值不同於Connection.getHoldability()提供的值,那麼此建議可能不適用。

+0

不,MySQL JDBC驅動程序只用於控制提取大小非常有限的支持得到的結果數。默認行爲是一次獲取select的整個結果。要傳輸結果,必須將讀取大小設置爲Integer.MIN_VALUE,並使用ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY創建Statement。這樣做,有幾個其他限制必須考慮,如文檔中指出:http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html – jarnbjo

+0

我已經使用了這些代碼: st.setFetchSize(500); rsDBReader.setFetchSize(500); 但它沒有改變任何東西 – Soheil

+0

@ jarnbjo,謝謝。原來我只是部分正確。 –

-1

你看到的其實是正常現象,不必一定表示內存泄漏。 Java中的對象實例在它們變得無法訪問後不會立即收集垃圾,大多數Java虛擬機很不願意將一次分配的內存返回給操作系統。

如果您使用的是最新的Oracle的Java虛擬機的版本,確實需要更積極的垃圾收集器,您可以通過添加以下參數到java命令嘗試G1GC實現:

-XX:+ UnlockExperimentalVMOptions - XX:+ UseG1GC

的G1GC垃圾收集器通常回收對象的速度比默認的垃圾收集和未使用的存儲器也由過程釋放。

0

注意有類似的問題與的Postgres的最新版本。爲了實現*你需要禁用自動提交對連接connection.setAutoCommit(false),並在您的SQL語句(只包含一個分號即語句)使用單個語句遊標處理。它爲我工作。

Postgres JDBC documentation