2011-11-28 80 views
1

我的應用程序由於使用JDBC而導致內存泄漏。我通過查看堆的可視轉儲並查看數以千計的ResultSet和關聯對象的實例來驗證這一點。那麼我的問題是,如何恰當地管理JDBC使用的資源,以便它們可以被垃圾收集?我是否需要爲每個使用的語句調用「.close()」?我需要在ResultSets上自己調用「.close()」嗎?正在發佈JDBC資源

你會如何釋放由呼叫使用的內存:

ResultSet rs = connection.createStatement().executeQuery("some sql query"); 

我看到還有其他非常相似的問題。道歉,如果這是多餘的,但要麼我不完全按照答案或​​他們似乎並不普遍適用。我正試圖在使用JDBC時如何管理內存方面取得權威性的回答。

::編輯::添加一些代碼樣本

我有一個類,基本上是我用來簡化數據庫交互的JDBC助手,主要的兩種方法執行插入或更新,以及執行選擇語句。

這一個用於執行INSERT或UPDATE語句:

public int executeCommand(String sqlCommand) throws SQLException { 
    if (connection == null || connection.isClosed()) { 
     sqlConnect(); 
    } 
    Statement st = connection.createStatement(); 
    int ret = st.executeUpdate(sqlCommand); 
    st.close(); 
    return ret; 
} 

而這一次從一個SELECT返回結果集:

public ResultSet executeSelect(String select) throws SQLException { 
    if (connection == null || connection.isClosed()) { 
     sqlConnect(); 
    } 
    ResultSet rs = connection.createStatement().executeQuery(select); 
    return rs; 
} 

使用的ExecuteSelect()方法後,我總是通話resultset.getStatement()。close()

使用顯示語句上的對象分配跟蹤檢查堆轉儲仍然保持從這兩種方法...

+1

請務必關閉ResultSet和語句'嘗試{} finally'塊,都設置爲null。如果您添加了您使用的典型jdbc執行代碼,則使用這些代碼的代碼應位於代碼塊 – aishwarya

+0

之內,如果您未發佈任何jdbc資源,對我們進行評論可能會更有幫助。 –

回答

2

如果你不打算重用它,你應該關閉聲明。首先關閉ResultSet通常是很好的形式,因爲有些實現沒有自動關閉ResultSet(即使它們應該)。

如果您重複相同的查詢,您應該使用PreparedStatement來減少解析開銷。如果你添加參數到你的查詢中,你應該使用PreparedStatement來避免sql注入的風險。

2

是的,ResultSets和Statements應該總是在finally塊中關閉。使用諸如Spring的JdbcTemplate之類的JDBC包裝器可以減少代碼冗長併爲您關閉所有內容。

1

我從我一直在努力的項目中複製這個。我正在重構它的過程中使用Hibernate(從代碼中應該清楚爲什麼!!)。使用像Hibernate這樣的ORM工具是解決你的問題的一種方法。否則,這是我使用正常的DAO訪問數據的方式。我們的代碼中沒有內存泄漏,所以這可能有助於作爲模板。希望它有幫助,內存泄漏是可怕的!

@Override 
public List<CampaignsDTO> getCampaign(String key) { 
    ResultSet resultSet = null; 
    PreparedStatement statement = null; 
    try { 
     statement = connection.prepareStatement(getSQL("CampaignsDAOImpl.getPendingCampaigns")); 
     statement.setString(1, key); 
     resultSet = statement.executeQuery(); 

     List<CampaignsDTO> list = new ArrayList<CampaignsDTO>(); 

     while (resultSet.next()) { 
      list.add(new CampaignsDTO(
        resultSet.getTimestamp(resultSet.findColumn("cmp_name")), 
        ...)); 
     } 
     return list; 
    } catch (SQLException e) { 
     logger.fatal(LoggerCodes.DATABASE_ERROR, e); 
     throw new RuntimeException(e); 
    } finally { 
     close(statement); 
    } 
} 

的close()方法是這樣的:

public void close(PreparedStatement statement) { 
    try { 
     if (statement != null && !statement.isClosed()) 
      statement.close(); 
    } catch (SQLException e) { 
     logger.debug(LoggerCodes.TRACE, "Warning! PreparedStatement could not be closed."); 
    } 
} 
0

你應該關閉JDBC語句時,你就完成了。 ResultSets應該在關聯語句關閉時被釋放 - 但如果你願意,你可以明確地做到這一點。

您需要確保在異常情況下關閉所有JDBC資源。

使用try-catch-finally塊 - 例如:

try { 
    conn = dataSource.getConnection(); 
    stmt = conn.createStatement(); 
    rs = stmet.executeQuery("select * from sometable"); 
    stmt.close(); 
    conn.close(); 
} catch (Throwable t) { 
    // do error handling 
} finally { 
    try { 
     if (stmt != null) { 
      stmt.close(); 
     } 
     if (conn != null) { 
      conn.close(); 
     } 
    } catch(Exception e) { 

    } 
} 
+0

此代碼有問題...如果stmt.close()拋出SQLException,則不會調用conn.close()。你需要用單獨的try/catch塊來包裝stmt.close()和conn.close()。所有更多的理由將所有這些樣板代碼抽象出來......就像其他地方提到的那樣,Spring的JdbcTemplate是一個不錯的選擇。 –