內存泄露簡介
Java的最顯著的優點中包含了一點,內存管理。 你只需要創建對象,Java的垃圾回收機制會負責分配和釋放內存。然而實際情況并沒有那么簡單,因為在Java應用中會發生內存泄露。 這個帖子簡要的說明了什么是內存泄露,它為什么會發生,怎樣防止它發生。
1. 什么是內存泄露?
內存泄露: 對象不再被使用,但是又有引用指向它,所以不能被GC回收。
為了更清楚的理解這個定義,我們需要知道對象在內存中的狀態。下面這個插圖將對象分為兩種狀態,被引用的和未被引用的。其中被引用的對象中有一部分是沒被使用的。內存泄露就發生在這些對象所在的內存區域。
2. 為什么會發生內存泄露?
讓我們來看接下來這個例子,它會告訴我們為什么會發生內存泄露。在這個例子中,對象A指向對象B。對象A的生命周期是(t1-t4)比對象B的生命周期(t2-t3)長很多。當對象B不再被程序所使用的時候,對象A依然指向它。這樣的話GC就不能從內存中回收對象B。這樣的話就可能會引起內存溢出。因為如果對象A指向很多個這樣的對象,內存中就會存在很多不能被回收內存空間的對象。
還有另外一種情況,B指向了其它的很多對象,導致了其它的對象也不能被回收。
3.怎樣預防內存泄露?
以下有幾點預防內存泄露的小建議:
-
留心使用集合類,如:HashMap,ArrayList,因為內存泄露通常是它們引起的。當它們被聲明為
static
,它們的生命周期就跟應用的生命周期相同。 -
留心使用事件監聽和回調函數。如果監聽被注冊了之后但是該類不再被使用的時候沒有注銷也會引起內存泄露。
-
成員變量如果是對象的話,需要使用null來銷毀這個對象的引用。
4. 為什么JDK6中的substring()方法會導致內存泄露?
JDK6中subString()的源碼
//JDK 6 String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } public String substring(int beginIndex, int endIndex) { //check boundary return new String(offset + beginIndex, endIndex - beginIndex, value); }
實際上substring并沒有去new 一個String對象,substring返回的字符串和之前的字符串是共用的一個字符數組。
只是數組的起點和長度改變了。所以之前的那個被截取的字符串就沒有(也不能)被回收。
如果你想要讓它能被回收,可以這樣substring.
x.substring(a,b)+"" //or new String(x.substring(a,b))
前者等同于:
StringBuilder sb = new StringBuilder(); sb.append(x.substring(x, y)); sb.append(""); x = sb.toString();
所以,使用new String(x.substring(a,b))的方式效率更高。
原文:http://www.programcreek.com/2013/10/the-introduction-of-memory-leak-what-why-and-how/