Java最顯著的優點之一就是它的記憶體管理機制。你只需簡單建立對象,然後Java垃圾回收機製便會小心的分配和釋放記憶體。然而,事實並非如此簡單,因為在Java應用程式中經常發生記憶體洩漏。
本教學說明了什麼是記憶體洩漏,為什麼會發生,以及如何防止它們。
1.什麼是記憶體洩漏?
記憶體洩漏的定義: 物件不再被應用程式使用,但是垃圾回收器卻不能移除它們,因為它們正在被引用。
要理解這個定義,我們需要理解物件在記憶體中的狀態,未被引用的物件將會被垃圾回收器回收,而被引用物件則不會被回收。未被引用的物件理所當然是未被使用的,因為沒有其他的物件引用它。然而,未被使用的物件並不一定是未被引用的,其中一些是被引用的。這就是記憶體洩漏的起因。
2.為什麼會發生記憶體洩漏?
讓我們來看看下面這個例子,看看為什麼記憶體洩漏會發生。在例子中,物件A引用了物件B。 A的生命週期(t1—t4)比B的生命週期(t2—t3)長很多。當B不再用於應用程式時,A仍然持有對它的引用。在這種方式下,垃圾回收器就不能將B從記憶體中移除。這將可能導致出現記憶體不足的問題,因為如果A對更多的物件做同樣的事情,那麼記憶體中將會有很多無法被回收的對象,這將極度耗費記憶體空間。
也有可能B持有大量對其他物件的引用,這些被B引用的物件也不能夠被回收。所有這些未被使用的物件將會耗費寶貴的記憶體空間。
3.如何阻止記憶體洩漏?
以下是一些阻止記憶體洩漏的快速動手技巧。
(1)注意集合類,例如HashMap,ArrayList,等等。因為它們是內存洩漏經常發生的地方。當它們被聲明為靜態時,它們的生命週期就同應用程式的生命週期一般長。
(2)注意事件監聽器和回調,如果一個監聽器已經註冊,但是當這個類別不再被使用時卻未被註銷,就會發生記憶體洩漏。
(3)「如果一個類別管理它自己的內存,程式設計師應該對記憶體洩漏保持警惕。」[1] 很多時候當一個物件的成員變數指向其他物件時,不再使用時需要被置為null 。
4.一個小測驗:為什麼在JDK6中substring()方法會造成記憶體洩漏?
為了回答這個問題,您可能需要閱讀JDK6和7中的substring()。
參考文獻:
[1]Bloch,Joshua.Effective Java.Addison-Wesley Professional, 2008
[2]IBM Developer Work.http://www.ibm.com/developerworks/library/j-leaks/