2012-01-11 145 views
1

我在weblogic上使用spring jdbc。我把抓取大小設置爲500,以便更快地從數據庫獲取數據。但是這會導致記憶問題。這裏有一個例子:如何通過jdbc獲取數據後釋放內存

http://webmoli.com/2009/02/01/jdbc-performance-tuning-with-optimal-fetch-size/

我的問題是如何釋放這個內存?運行GC不工作,我猜測它不工作,因爲連接池中的連接已經存在。

代碼:

public List<Msisdn> getNewMsisdnsForBulkSmsId(String bulkSmsId,String scheduleId,final int msisdnCount) throws SQLException { 
      JdbcTemplate jdbcTemplate = getJdbcTemplate(); 
      jdbcTemplate.setFetchSize(500); 
      jdbcTemplate.setMaxRows(msisdnCount); 
      jdbcTemplate.query("select BULKSMS_ID, ? as , STATUSSELECTDATE, DELIVERYTIME, ID, MESSAGE from ada_msisdn partition (ID_"+bulkSmsId+") where bulksms_id = ? and status = 0 and ERRORCODE = 0 and SCHEDULEID is null for update skip locked", new Object[]{scheduleId,bulkSmsId}, MsisdnRowMapper.INSTANCE); 

    //Also i tried to close connection and run gc, this does not free the memory too. 
      //jdbcTemplate.getDataSource().getConnection().close(); 
      //System.gc(); 

      return null; 
} 

當我設置提取大小爲10,堆大小爲12 MB 如果我設置提取大小爲500,堆大小爲206 MB

感謝名單

+1

如果您使用探查器,您應該能夠看到爲什麼保留此內存。 – 2012-01-11 16:48:56

+0

請發佈代碼。 – kosa 2012-01-11 16:49:51

+0

似乎連接正在分配內存,在我的代碼中沒有內存泄漏:60.9% - 206 MB - 561,359 alloc。 org.springframework.jdbc.core.JdbcTemplate.query – 2012-01-11 18:32:52

回答

1

更新爲增加示例代碼,等:

這聽起來像你只需要使用比500的值少了,但讓我覺得你是返回一個lot更多數據比您的結果集mapper實際使用的更多。

現在,我看到你存儲的映射結果所有List,我會說,與獲取大小看到的問題很可能是一個次要問題。 List<Msisdn>所需的組合內存空間和一組取自ResultSet行的組合內存空間正在推動您超過可用內存。

什麼是msisdnCount價值?如果它大於500,那麼你可能在list中使用的內存比在ResultSet的500條記錄中多。如果它小於500,那麼我認爲當您將獲取大小設置爲msisdnCount時,也會出現內存問題,並且錯誤會在min(msisdnCount, 500)和10之間的某個值消失。

將所有結果加載到列表然後處理它們是一種很常導致內存耗盡的模式。常見的解決方案是使用流式傳輸。如果你可以處理每一行,因爲它來和而不是所有的地圖結果存儲在您的list,那麼你可以避免內存問題。

我沒有看到在Spring JDBC核心包中的任何數據流的支持,但如果我覺得我會及時更新。

-

如果您檢索的行數據是足夠的提取500行會使用你的堆,那麼您必須每行返回較少的數據或在同一時間獲取較少的行巨大。

您可能會發現,您正在代碼中某處存儲提取的行,這意味着它不是使用內存的ResultSet。例如,您可能正在將所有行復制到某個集合實例。

我會查看每行數據的大小,並嘗試減少可能包含大數據類型的不需要的列,然後嘗試簡單地加載數據並遍歷結果,而不進行正常處理,這可能會存儲將數據存放在某處,以查看您可以一次加載的內存數量。如果您的內存用完了500行,您必須將大量數據提取出來。如果你實際上沒有使用這些數據,那麼你會浪費CPU和網絡資源以及內存。

編輯:您可能還需要設置遊標行爲,以便讓JDBC驅動程序知道它可以拋棄的內容。例如,您可以使用ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY準備語句。 http://docs.oracle.com/javase/6/docs/api/index.html?java/sql/ResultSet.html

+0

我試過ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY並沒有任何效果 – 2012-01-11 18:36:35

+0

首先感謝您的回答。我改變了代碼,我沒有將查詢結果分配給任何變量。我剖析並看到分配206 MB的java.sql.Statement類。然後轉到weblogic控制檯並掛起連接池並運行GC。我發現內存恢復正常。 (206 MB解除分配)。所以,我的問題是如何在不關閉連接的情況下取消分配內存。 – 2012-01-14 21:20:47

+0

如果GC返回了內存,那麼表明'java.sql.Statement'不是持有內存的東西。根據Weblogic文檔,除非「暫停」連接池意味着您關閉了連接,而這隻會在「forceSuspend()」而不是「suspend()」時發生,否則我懷疑是上面列出的'List ':http ://docs.oracle.com/cd/E13222_01/wls/docs81/jdbc/programming.html#1055751如果你可以使用Spring'RowCallbackHandler'來處理每一行而不是'List',那麼你可能會減少你的內存使用量。 – jbindel 2012-01-15 21:13:32