1. 程式人生 > >【java記憶體洩漏5種情況總結】

【java記憶體洩漏5種情況總結】

記憶體洩漏定義:一個不再被程式使用的物件或變數還在記憶體中佔有儲存空間。

由於java的JVM引入了垃圾回收機制,垃圾回收器會自動回收不再使用的物件,瞭解JVM回收機制的都知道JVM是使用引用計數法和可達性分析演算法來判斷物件是否是不再使用的物件,本質都是判斷一個物件是否還被引用。那麼對於這種情況下,由於程式碼的實現不同就會出現很多種記憶體洩漏問題(讓JVM誤以為此物件還在引用中,無法回收,造成記憶體洩漏)。

1、靜態集合類,如HashMap、LinkedList等等。如果這些容器為靜態的,那麼它們的生命週期與程式一致,則容器中的物件在程式結束之前將不能被釋放,從而造成記憶體洩漏。簡單而言,長生命週期的物件持有短生命週期物件的引用,儘管短生命週期的物件不再使用,但是因為長生命週期物件持有它的引用而導致不能被回收。

2、各種連線,如資料庫連線、網路連線和IO連線等。在對資料庫進行操作的過程中,首先需要建立與資料庫的連線,當不再使用時,需要呼叫close方法來釋放與資料庫的連線。只有連線被關閉後,垃圾回收器才會回收對應的物件。否則,如果在訪問資料庫的過程中,對Connection、Statement或ResultSet不顯性地關閉,將會造成大量的物件無法被回收,從而引起記憶體洩漏。

3、變數不合理的作用域。一般而言,一個變數的定義的作用範圍大於其使用範圍,很有可能會造成記憶體洩漏。另一方面,如果沒有及時地把物件設定為null,很有可能導致記憶體洩漏的發生。

public class UsingRandom {
	
	private String msg;
	public void receiveMsg(){
		readFromNet();// 從網路中接受資料儲存到msg中
		saveDB();// 把msg儲存到資料庫中
	}
}

如上面這個虛擬碼,通過readFromNet方法把接受的訊息儲存在變數msg中,然後呼叫saveDB方法把msg的內容儲存到資料庫中,此時msg已經就沒用了,由於msg的生命週期與物件的生命週期相同,此時msg還不能回收,因此造成了記憶體洩漏。

實際上這個msg變數可以放在receiveMsg方法內部,當方法使用完,那麼msg的生命週期也就結束,此時就可以回收了。還有一種方法,在使用完msg後,把msg設定為null,這樣垃圾回收器也會回收msg的記憶體空間。

4、內部類持有外部類,如果一個外部類的例項物件的方法返回了一個內部類的例項物件,這個內部類物件被長期引用了,即使那個外部類例項物件不再被使用,但由於內部類持有外部類的例項物件,這個外部類物件將不會被垃圾回收,這也會造成記憶體洩露。

5、改變雜湊值,當一個物件被儲存進HashSet集合中以後,就不能修改這個物件中的那些參與計算雜湊值的欄位了,否則,物件修改後的雜湊值與最初儲存進HashSet集合中時的雜湊值就不同了,在這種情況下,即使在contains方法使用該物件的當前引用作為的引數去HashSet集合中檢索物件,也將返回找不到物件的結果,這也會導致無法從HashSet集合中單獨刪除當前物件,造成記憶體洩露