Java內存泄漏的幾大原因及預防檢測
來自: http://blog.csdn.net//chenleixing/article/details/43228809
??
Java 中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java 中的內存泄漏,這些對象不會被GC 所回收,然而它卻占用內存。在C++中,內存泄漏的范圍更大一些。有些對象被分配了內存空間,然后卻不可達,由于C++中沒有GC,這些內存將永遠收不回來。在Java 中,這些不可達的對象都由GC 負責回收,因此程序員不需要考慮這部分的內存泄漏。通過分析,可以得知,對于C++,程序員需要自己管理邊和頂點,而對于Java 程序員只需要管理邊就可以了(不需要管理頂點的釋放),通過這種方式,Java 提高了編程的效率。內存泄漏示例
示例1
在這個例子中,循環申請Object 對象,并將所申請的對象放入一個Vector 中,如果僅僅釋放引用本身,那么Vector 仍然引用該對象,所以這個對象對GC 來說是不可回收的。因此,如果對象加入到Vector 后,還必須從Vector 中刪除,最簡單的方法就是將Vector對象設置為null。
Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{Object o = new Object();
v.add(o);
o = null;
}//
此時,所有的Object 對象都沒有被釋放,因為變量v 引用這些對象。實際上無用,而還被引用的對象,GC 就無能為力了(事實上GC 認為它還有用),這一點是導致內存泄漏最重要的原因。
(1)如果要釋放對象,就必須使其的引用記數為0,只有那些不再被引用的對象才能被釋放,這個原理很簡單,但是很重要,是導致內存泄漏的基本原因,也是解決內存泄漏方法的宗旨;
(2)程序員無須管理對象空間具體的分配和釋放過程,但必須要關注被釋放對象的引用記數是否為0;
(3)一個對象可能被其他對象引用的過程的幾種:
a.直接賦值,如上例中的A.a = E;
b.通過參數傳遞,例如public void addObject(Object E);
c.其它一些情況如系統調用等。
容易引起內存泄漏的幾大原因
靜態集合類
像HashMap、Vector 等靜態集合類的使用最容易引起內存泄漏,因為這些靜態變量的生命周期與應用程序一致,如示例1,如果該Vector 是靜態的,那么它將一直存在,而其中所有的Object對象也不能被釋放,因為它們也將一直被該Vector 引用著。
監聽器
在java 編程中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會調用一個控件的諸如addXXXListener()等方法來增加監聽器,但往往在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增加了內存泄漏的機會。
物理連接
一些物理連接,比如數據庫連接和網絡連接,除非其顯式的關閉了連接,否則是不會自動被GC 回收的。Java 數據庫連接一般用DataSource.getConnection()來創建,當不再使用時必須用Close()方法來釋放,因為這些連接是獨立于JVM的。對于Resultset 和Statement 對象可以不進行顯式回收,但Connection 一定要顯式回收,因為Connection 在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會立即為NULL。但是如果使用連接池,情況就不一樣了,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 對象無法釋放,從而引起內存泄漏。
內部類和外部模塊等的引用
內部類的引用是比較容易遺忘的一種,而且一旦沒釋放可能導致一系列的后繼類對象沒有釋放。對于程序員而言,自己的程序很清楚,如果發現內存泄漏,自己對這些對象的引用可以很快定位并解決,但是現在的應用軟件
并非一個人實現,模塊化的思想在現代軟件中非常明顯,所以程序員要小心外部模塊不經意的引用,例如程序員A 負責A 模塊,調用了B 模塊的一個方法如:
public void registerMsg(Object b);
這種調用就要非常小心了,傳入了一個對象,很可能模塊B就保持了對該對象的引用,這時候就需要注意模塊B 是否提供相應的操作去除引用。
預防和檢測內存漏洞
在了解了引起內存泄漏的一些原因后,應該盡可能地避免和發現內存泄漏。
(1)好的編碼習慣。最基本的建議就是盡早釋放無用對象的引用,大多數程序員在使用臨時變量的時候,都是讓引用變量在退出活動域后,自動設置為null。在使用這種方式時候,必須特別注意一些復雜的對象圖,例如數組、列、樹、圖等,這些對象之間有相互引用關系較為復雜。對于這類對象,GC 回收它們一般效率較低。如果程序允許,盡早將不用的引用對象賦為null。另外建議幾點:
在確認一個對象無用后,將其所有引用顯式的置為null;
當類從Jpanel 或Jdialog 或其它容器類繼承的時候,刪除該對象之前不妨調用它的removeall()方法;在設一個引用變量為null 值之前,應注意該引用變量指向的對象是否被監聽,若有,要首先除去監聽器,然后才可以賦空值;當對象是一個Thread 的時候,刪除該對象之前不妨調用它的interrupt()方法;內存檢測過程中不僅要關注自己編寫的類對象,同時也要關注一些基本類型的對象,例如:int[]、String、char[]等等;如果有數據庫連接,使用try…finally 結構,在finally 中關閉Statement 對象和連接。
(2)好的測試工具。在開發中不能完全避免內存泄漏,關鍵要在發現有內存泄漏的時候能用好的測試工具迅速定位問題的所在。市場上已有幾種專業檢查Java 內存泄漏的工具,它們的基本工作原理大同小異,都是通過監測Java 程序運行時,所有對象的申請、釋放等動作,將內存管理的所有信息進行統計、分析、可視化。開發人員將根據這些信息判斷程序是否有內存泄漏問題。這些工具包括Optimizeit Profiler、JProbe Profiler、JinSight、Rational 公司的Purify 等。</span>
本文由用戶 CanShelly 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!