java十分鐘速懂知識點——引用

1、由健忘症引發的問題

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

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的引用以後,發現吧指針等同於引用是有一些問題的。
學習

2、引用究竟是什麼

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

  • 自己是一種數據類型,存儲在棧區
  • 其值存儲着實例對象在堆區的虛擬地址(注意,是虛擬地址,並非實際內存地址,就如同圖書館裏的索引號,不通過轉換你並不知道書的實際位置
  • 對象在建立未賦值時(無實例),引用會指向null
  • java中參數傳遞只有值傳遞一種,所謂的引用傳遞傳遞的是引用中存儲的值

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

3、上邊的例子到底發生了什麼

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


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

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

4、只爲告終尾不突兀

    原本是想整理篇內存溢出和內存泄露的,順便寫下這塊,可是發現並非太好說清楚,就單寫了。好了,就這樣吧,原來JVM學習總結系列寫了一半扔那了,如今發現仍是挺有意思的,有空接着寫吧。
相關文章
相關標籤/搜索