JDBC中關於Connection, PreparedStatement, ResultSet是否關閉的一些思考

如果你不使用連接池,那麼就沒有什麼問題,一旦Connection關閉,數據庫物理連接就被釋放,所有相關Java資源也可以被GC回收了。但是如果你使用連接池,那麼請注意,Connection關閉並不是物理關閉,只是歸還連接池,所以PreparedStatement和ResultSet都被持有,並且實際佔用相關的數據庫的遊標資源,在這種情況下,只要長期運行,往往就會報“遊標超出數據庫允許的最大值”的錯誤,導致程序無法正常訪問數據庫。

在使用java開發後臺應用程序的時候,如果需要使用數據庫,特別是試用第三方的數據庫連接池的時候,使用完PreparedStatement等一定要手動關閉,最好是將關閉的代碼寫到finally中,保證一定能夠完成關閉。   

原因有如下兩點:

1、第三方的數據庫連接池,使用的時候,獲取到Connection之後,使用完成,調用的關閉方法(close()) ,並沒有將Connection關閉,只是放回到連接池中,如果調用的這個方法,而沒有手動關閉PreparedStatement等,則這個PreparedStatement並沒有關閉,這樣會使得開發的程序內存急速增長,java的內存回收機制可能跟不上速度,最終造成Out of memory Error。

2、如過在PreparedStatement等調用的時候,發生異常,則這個PreparedStatement是沒有被關閉的,因此最好將PreparedStatement等的關閉寫到finally代碼中。下面是本人

在本機上試過的一個測試實例

	public static List<OrgzMetaDto> queryMetaByOrgzName1() {
		String sql = "SELECT * FROM test_table limit 0, 10000";
		List<OrgzMetaDto> orgzs = new ArrayList<OrgzMetaDto>();
		try {
			pst = conn.prepareStatement(sql);
			ResultSet resultSet = pst.executeQuery();
			while (resultSet.next()) {
				OrgzMetaDto dto = new OrgzMetaDto();
				dto.setUuid(resultSet.getString("uuid"));
				orgzs.add(dto);
			}
                        resultSet.close();
	 	} catch (Exception e) {
			e.printStackTrace();
		}
		return orgzs;
	}
	
	public static void main(String[] args) throws Exception {
		for (int i=0; i<1000; i++) {
			System.out.println(i);
			queryMetaByOrgzName1();
		}
	}

可以自行用類似於上述的代碼測試一下 , 測試的時候關注本機物理內存的佔用狀況
在有resultSet.close()這句代碼的時候,內存佔用並不會發生太大變化,而一旦不關閉結果集,內存佔用將會急速增長,甚至程序被hang住,我猜測可能就是上述所說的“遊標超出數據庫允許的最大值”,就算不阻塞,內存佔用繼續增長,將出現內存溢出問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章