java內存泄露

原文地址:http://leihuang.org/2014/05/18/Dynamic-Memory-Allocation/


什麼是內存泄露

當某些對象不再被應用程序所使用,但是由於仍然被引用而導致垃圾收集器不能釋放它們。

下圖中就是內存泄露的情形。 

img

圖中的中間部分就是內存泄露的發生地。


爲什麼會內存泄露

讓我們來分析一下下圖中的例子,看看爲什麼會發生內存泄露。對象A時B的引用。A的生命週期時t1-t4,而B的生命週期時t2-t3.所以當B在程序中不在使用時,A依然存在一個引用指向B,此時B就不能被GC回收。這樣就可能引起內存泄露,因爲A有可能對其他對象也做了同樣的事情,同時B也可能存在指向多個對象的引用,這樣內存是肯定不夠的。 img


java是如何管理內存

從上面我們已經大致的理解了內存泄露大體是個什麼情況,但java到底如何管理內存的呢。

Java的內存管理就是對象的分配和釋放問題。在Java中,程序員需要通過關鍵字new爲每個對象申請內存空間 (基本類型除外),所有的對象都在堆 (Heap)中分配空間。另外,對象的釋放是由GC決定和執行的。在Java中,內存的分配是由程序完成的,而內存的釋放是有GC完成的,這種收支兩條線的方法確實簡化了程序員的工作。但同時,它也加重了JVM的工作。這也是Java程序運行速度較慢的原因之一。因爲,GC爲了能夠正確釋放對象,GC必須監控每一個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC都需要進行監控。

監視對象狀態是爲了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象不再被引用。

爲了更好理解GC的工作原理,我們可以將對象考慮爲有向圖的頂點,將引用關係考慮爲圖的有向邊,有向邊從引用者指向被引對象。另外,每個線程對象可以作爲一個圖的起始頂點,例如大多程序從main進程開始執行,那麼該圖就是以main進程頂點開始的一棵根樹。在這個有向圖中,根頂點可達的對象都是有效對象,GC將不回收這些對象。如果某個對象 (連通子圖)與這個根頂點不可達(注意,該圖爲有向圖),那麼我們認爲這個(這些)對象不再被引用,可以被GC回收。

以下,我們舉一個例子說明如何用有向圖表示內存管理。對於程序的每一個時刻,我們都有一個有向圖表示JVM的內存分配情況。以下右圖,就是左邊程序運行到第6行的示意圖。 img

Java使用有向圖的方式進行內存管理,可以消除引用循環的問題,例如有三個對象,相互引用,只要它們和根進程不可達的,那麼GC也是可以回收它們的。這種方式的優點是管理內存的精度很高,但是效率較低。另外一種常用的內存管理技術是使用計數器,例如COM模型採用計數器方式管理構件,它與有向圖相比,精度行低(很難處理循環引用的問題),但執行效率很高。


深入內存泄露

下面,我們就可以描述什麼是內存泄漏。在Java中,內存泄漏就是存在一些被分配的對象,這些對象有下面兩個特點,首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連;其次,這些對象是無用的,即程序以後不會再使用這些對象。如果對象滿足這兩個條件,這些對象就可以判定爲Java中的內存泄漏,這些對象不會被GC所回收,然而它卻佔用內存。

在C++中,內存泄漏的範圍更大一些。有些對象被分配了內存空間,然後卻不可達,由於C++中沒有GC,這些內存將永遠收不回來。在Java中,這些不可達的對象都由GC負責回收,因此程序員不需要考慮這部分的內存泄露。

通過分析,我們得知,對於C++,程序員需要自己管理邊和頂點,而對於Java程序員只需要管理邊就可以了(不需要管理頂點的釋放)。通過這種方式,Java提高了編程的效率。 img

下面看effective java中的一段優化出棧操作的代碼

public Object pop() {
    if (size == 0)
    throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; // Eliminate obsolete reference
    return result;
}


代碼中elements[size] = null ;就是將原來指向出棧元素的引用刪除。這樣就不會存在一個引用指向不在需要使用的數據了(注意如果沒有這一行,則僅僅時將size的值減少了一個)。


如何防止內存泄露

The following are some quick hands-on tips for preventing memory leaks.

  1. Pay attention to Collection classes, such as HashMap, ArrayList, etc., as they are common places to find memory leaks. When they are declared static, their life time is the same as the life time of the application.

  2. Pay attention to event listeners and callbacks. A memory leak may occur if a listener is registered but not unregistered when the class is not being used any longer.

  3. "If a class manages its own memory, the programer should be alert for memory leaks."[1] Often times member variables of an object that point to other objects need to be null out.


Why substring() method in JDK 6 can cause memory leaks?

詳見:The substring() Method in JDK 6 and JDK 7

綜上所述,Java也存在內存泄露問題,其原因主要是一些對象雖然不再被使用,但它們仍然被引用。


2014-11-10 18:59:04

Brave,Happy,Thanksgiving !


發佈了142 篇原創文章 · 獲贊 104 · 訪問量 87萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章