詳細介紹Java的內(nèi)存管理與內(nèi)存泄露
作為Internet***的編程語言之一,Java現(xiàn)正非常流行。我們的網(wǎng)絡(luò)應(yīng)用程序就主要采用Java語言開發(fā),大體上分為客戶端、服務(wù)器和數(shù)據(jù)庫三個層次。在進入測試過程中,我們發(fā)現(xiàn)有一個程序模塊系統(tǒng)內(nèi)存和CPU資源消耗急劇增加,持續(xù)增長到出現(xiàn)java.lang.OutOfMemoryError為止。經(jīng)過分析Java內(nèi)存泄漏是破壞系統(tǒng)的主要因素。這里與大家分享我們在開發(fā)過程中遇到的Java內(nèi)存泄漏的檢測和處理解決過程.
本文先介紹Java的內(nèi)存管理,以及導致Java內(nèi)存泄露的原因。
一. Java是如何管理內(nèi)存
為了判斷Java中是否有內(nèi)存泄露,我們首先必須了解Java是如何管理內(nèi)存的。Java的內(nèi)存管理就是對象的分配和釋放問題。在Java中,內(nèi)存的分配是由程序完成的,而內(nèi)存的釋放是由垃圾收集器(Garbage Collection,GC)完成的,程序員不需要通過調(diào)用函數(shù)來釋放內(nèi)存,但它只能回收無用并且不再被其它對象引用的那些對象所占用的空間。
Java的內(nèi)存垃圾回收機制是從程序的主要運行對象開始檢查引用鏈,當遍歷一遍后發(fā)現(xiàn)沒有被引用的孤立對象就作為垃圾回收。GC為了能夠正確釋放對象,必須監(jiān)控每一個對象的運行狀態(tài),包括對象的申請、引用、被引用、賦值等,GC都需要進行監(jiān)控。監(jiān)視對象狀態(tài)是為了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。
在Java中,這些無用的對象都由GC負責回收,因此程序員不需要考慮這部分的內(nèi)存泄露。雖然,我們有幾個函數(shù)可以訪問GC,例如運行GC的函數(shù)System.gc(),但是根據(jù)Java語言規(guī)范定義,該函數(shù)不保證JVM的垃圾收集器一定會執(zhí)行。因為不同的JVM實現(xiàn)者可能使用不同的算法管理GC。通常GC的線程的優(yōu)先級別較低。JVM調(diào)用GC的策略也有很多種,有的是內(nèi)存使用到達一定程度時,GC才開始工作,也有定時執(zhí)行的,有的是平緩執(zhí)行GC,有的是中斷式執(zhí)行GC。但通常來說,我們不需要關(guān)心這些。
二. 什么是Java中的內(nèi)存泄露
導致內(nèi)存泄漏主要的原因是,先前申請了內(nèi)存空間而忘記了釋放。如果程序中存在對無用對象的引用,那么這些對象就會駐留內(nèi)存,消耗內(nèi)存,因為無法讓垃圾回收器GC驗證這些對象是否不再需要。如果存在對象的引用,這個對象就被定義為"有效的活動",同時不會被釋放。要確定對象所占內(nèi)存將被回收,我們就要務(wù)必確認該對象不再會被使用。典型的做法就是把對象數(shù)據(jù)成員設(shè)為null或者從集合中移除該對象。但當局部變量不需要時,不需明顯的設(shè)為null,因為一個方法執(zhí)行完畢時,這些引用會自動被清理。
在Java中,內(nèi)存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是有被引用的,即在有向樹形圖中,存在樹枝通路可以與其相連;其次,這些對象是無用的,即程序以后不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定為Java中的內(nèi)存泄漏,這些對象不會被GC所回收,然而它卻占用內(nèi)存。
這里引用一個常看到的例子,在下面的代碼中,循環(huán)申請Object對象,并將所申請的對象放入一個Vector中,如果僅僅釋放對象本身,但因為Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設(shè)置為null。
- Vector v = new Vector(10);
 - for (int i = 1; i < 100; i++)
 - {
 - Object o = new Object();
 - v.add(o);
 - o = null;
 - }//此時,所有的Object對象都沒有被釋放,因為變量v引用這些對象。
 
實際上這些對象已經(jīng)是無用的,但還被引用,GC就無能為力了(事實上GC認為它還有用),這一點是導致內(nèi)存泄漏最重要的原因。 再引用另一個例子來說明Java的內(nèi)存泄漏。假設(shè)有一個日志類Logger,其提供一個靜態(tài)的log(String msg),任何其它類都可以調(diào)用Logger.Log(message)來將message的內(nèi)容記錄到系統(tǒng)的日志文件中。
Logger類有一個類型為HashMap的靜態(tài)變量temp,每次在執(zhí)行l(wèi)og(message)的時候,都首先將message的值寫入temp中(以當前線程+當前時間為鍵),在退出之前再從temp中將以當前線程和當前時間為鍵的條目刪除。注意,這里當前時間是不斷變化的,所以log在退出之前執(zhí)行刪除條目的操作并不能刪除執(zhí)行之初寫入的條目。這樣,任何一個作為參數(shù)傳給log的字符串最終由于被Logger的靜態(tài)變量temp引用,而無法得到回收,這種對象保持就是我們所說的Java內(nèi)存泄漏。 總的來說,內(nèi)存管理中的內(nèi)存泄漏產(chǎn)生的主要原因:保留下來卻永遠不再使用的對象引用。
【編輯推薦】















 
 
 




 
 
 
 