java中內存泄露(引用)

 

    閒來沒事在日誌中瞟見了個OutOfMemoryError錯誤,不禁得想到前一段時間看到一篇問到Java中是否有內存泄露,這個好久之前是留意過的,大致記得內存溢出和內存泄露是不一樣的,至於各自都有哪些狀況,那個.......忘了...。好吧,記憶力一貫很差,忘就忘了,那就再總結一遍吧。翻了下收藏的博客,回顧了下即是想起了了~.~。,可是其中的一個例子忽然使我困惑了: java

1 學習

2 spa

3 .net

4 指針

5 日誌

6 對象

7 索引

8 生命週期

9 內存

10

11

12

13

14

public class TestDemo {

    static Test[] tests = new Test[3];

    public  static void main(String[] args) {

        Test t = new Test("test1");

        tests[0] = t;

          //t置爲null,看起來彷佛咱們已經釋放建立的對象,當下次gc時其將被回收         t = null;

          //那麼咱們打印下test[0]看看         System.out.println(tests[0]);

    }

}

    這是個示例內存泄露的例子,該例子十分典型,幾乎全部內存泄露的示例都與此相似,做爲javaer每每以爲理所應當。然而做爲一個學習C++入行(學的很爛),並一直把引用當指針看的javaer難免以爲有些疑惑:t是對象的引用,這裏能夠看作指向對象的指針,那麼test[0]=t,按理說應該是把t指針賦值給test[0],算是地址傳遞吧,那個t指向null以後,test[0]應該也指向null了啊。
   
看起來彷佛有點道理,然而當了解了java的引用以後,發現吧指針等同於引用是有一些問題的。

 

    java中的引用究竟是什麼呢,簡單點說,引用就是存在棧區的一種特定類型的數據,其存儲着對象實例在堆區的地址,其特色以下:

·         自己是一種數據類型,存儲在棧區

·         其值存儲着實例對象在堆區的虛擬地址(注意,是虛擬地址,並非實際內存地址,就如同圖書館裏的索引號,不通過轉換你並不知道書的實際位置

·         對象在建立未賦值時(無實例),引用會指向null

·         java中參數傳遞只有值傳遞一種,所謂的引用傳遞(準確說是共享傳遞)傳遞的是引用中存儲的值

    從定義看起來彷佛仍是區分不出來引用到底和指針有什麼區別,那麼請注意上邊紅字,java爲了屏蔽對內存直接操做,對對象的實際內存地址進行了包裝,從而使引用中的值只能用來找對象,而沒法操做內存。這一點正是和指針最大不一樣,C++中的指針就是一個真實的內存地址,能夠經過該地址把內存玩出十八般花樣。這點也說明了咱們經常把引用傳遞當成地址傳遞是錯誤的(雖然說實際效果差很少)。

    好吧,看了上邊一坨也許你並看不出個什麼,也許自己這塊有點繞,也許我說的不清楚,那麼咱們不如直接畫圖說明上邊那個例子到底發生了什麼(圖示畫的不必定和實際徹底一致,只爲說明問題),說不定你就明白了:
    tests
因爲是靜態變量,在類加載完就已經實例化了,其在堆內存中分配了長度爲3的空間,不過值都爲null。在建立t以後,t指向了堆內存中的對象:

    tests[0]=t,這就是咱們理解錯誤的地方,這一步test[0]並非指向t,而是tTest實例的地址直接賦值給了tests[0],所以tests[0]一樣指向Test實例,這和t已經沒有任何關係了。

    其實從上圖咱們就應該理解了,t=null以後,其實只是斬斷了tTest實例的關係,並無改變Testtests的依賴,從而gc並不會回收Test,這樣就形成了邏輯上的內存泄露(爲啥說邏輯上,由於明明就是你讓tests還存着Test呢,只是你自覺得是的覺得釋放了,固然,這種意義的泄露和C++所說的內存泄露很不一樣)。

java內存溢出場景:
    Java 也是有內存泄露的,雖然Java有著名的GC( 垃圾回收機制),因爲Java是經過程序來分配內存空間,釋放則交給GC, 因爲程序編寫不當,仍然會引發內存泄露。

    Java中的內存泄露的狀況:長生命週期的對象持有短生命週期對象的引用就極可能發生內存泄露,儘管短生命週期對象已經再也不須要,可是由於長生命週期對象持有它的引用而致使不能被回收,這就是java中內存泄露的發生場景。例如在Application中保存一個對象,這個對象其實沒有使用了,可是因爲一直被引用,形成不能回收。     內存泄露的狀況是:一個外部類的實例對象的方法返回了一個內部類的實例對象,這個內部類對象被長期引用了,即便那個外部類實例對象再也不被使用,但因爲內部類持久外部類的實例對象,這個外部類對象將不會被垃圾回收。     內存泄露的另一種狀況:當一個對象被存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段了,不然,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不一樣了,這也會致使沒法從HashSet集合中單獨刪除當前對象,形成內存泄露

相關文章
相關標籤/搜索