1.什么是內存泄露
內存泄露是當某些對象不再被應用程序所使用,而垃圾回收器(GC)沒有識別到這些對象不再使用(),從而這些對象無法被垃圾回收器回收,占用的內存空間無法得到釋放.
程序運行過程中會不斷的分配內存空間,那些不再使用的內存空間應該及時地回收,如果存在無用的內存沒有被回收,這就是內存泄露.
2.什么引起內存泄露
上文中還存在一些疑點,GC就是負責回收不再使用的對象的,那么某些對象不再使用了,為什么GC沒有把它們回收呢?
這就需要從GC的回收機制談起:Java使用引用(Reference)的方式訪問對象,那么如果ObjectA不再有引用,這時GC就會判定ObjectA是可以回收的,在特定的時刻就會進行回收.比如
1 package test; 2 3 public class ObjectGarbage { 4 5 public static void main(String[] args) { 6 Object objectA = new Object(); 7 Object objectB = new Object(); 8 9 objectB = objectA;//將ObjectA的引用指向ObjectB 10 } 11 12 }

當一個對象失去所有的引用后,GC就可以將其回收,比如此圖堆中的Object.反過來說,當不再使用的對象還存在引用時,GC就不能將其回收.這就跟住賓館一樣,住賓館需要領房卡,只要我沒有退房卡,即時這間房間我沒有使用,賓館服務員就不能將這件房間進行回收,讓其他房客入住.
見到一篇文章中描述的非常好:
在下面的情境中,對象A引用了對象B. A的生命周期(t1 - t4) 比 B的(t2 - t3)要長得多. 當對象B在應用程序邏輯中不會再被使用以后, 對象 A 仍然持有着 B的引用. (根據虛擬機規范)在這種情況下垃圾收集器不能將 B 從內存中釋放. 這種情況很可能會引起內存問題,假若A 還持有着其他對象的引用,那么這些被引用的(無用)對象都不會被回收,並占用着內存空間.
甚至有可能 B 也持有一大堆其他對象的引用。 這些對象由於被 B 所引用,也不會被垃圾收集器所回收. 所有這些無用的對象將消耗大量寶貴的內存空間。

3.內存泄露舉例
看到《瘋狂Java》中一個很好的例子,在這里描述一下,ArrayList在remove最后一個元素的時候,只需將長度減一即可.比如,先前有四個元素,為刪除最后一個元素,將長度變為三,從功能上來說已經完成了刪除最后一個元素的目的.但是需要注意,此時ArrayList實例仍然有最后一個元素的引用,但最后一個元素對於ArrayList而言是不會使用的,而GC也不會回收第四個元素.此時就造成了內存泄露.
4.網上內容辨析
在寫這篇文章時候,也閱讀了不少網上的文章,看到一個例子:
1 Vector v = new Vector(10); 2 for (int i = 1; i < 100; i++) { 3 Object o = new Object(); 4 v.add(o); 5 o = null; 6 }//不推薦做為例子
在這個例子中,我們循環申請Object對象,並將所申請的對象放入一個Vector中,如果我們僅僅釋放引用本身,那么Vector仍然引用該對象,所以這個對象對GC來說是不可回收的。因此,如果對象加入到Vector后,還必須從Vector中刪除,最簡單的方法就是將Vector對象設置為null。
文中可能的意思是,o=null,表示o已經沒用了,生命周期就應該結束了,而v仍然引用着,GC無法回收,故內存泄露.
我在這里有個疑問,因為v的生命周期還沒有結束,此時我想取出第1個元素使用,是可以正確的取出來的,那為什么說Object無用呢?GC在此處沒有回收Object是正確的決定.而如果v的生命周期結束了,o就會回收.因此,對於一般的強引用,並不需要特別刻意地去處理它,GC已經能夠做得很好了,需要注意的是Java的類定義中隱藏的對象引用。
在此,希望大家不要人雲亦雲.
5.防止內存泄露
內存泄露,說白了就是,長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收.
需要注意一下幾方面:
1)靜態變量
靜態變量不是實例變量而是類變量,生命周期和應用程序一致,靜態變量引用的對象同樣不能得到釋放.
2)集合類
集合類僅僅有添加元素的方法,而沒有相應的刪除機制,導致內存被占用.這一點其實也不明確,這個集合類如果僅僅是局部變量,根本不會造成內存泄露,在方法棧退出后就沒有引用了會被jvm正常回收.而如果這個集合類是全局性的變量(比如類中的靜態屬性,全局性的map等),那么沒有相應的刪除機制,很可能導致集合所占用的內存只增不減,因此提供這樣的刪除機制或者定期清除策略非常必要.這里就說到緩存方面的內容.
3)單例模式
不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化后將在JVM的整個生命周期中存在(以靜態變量的方式),如果單例對象持有外部對象的引用,那么這個外部對象將不能被jvm正常回收,導致內存泄露.
相關文章:
1.What is a Memory Leak in Java?
4.《瘋狂Java》
