Java引用總結【草案】

來源:互聯網
上載者:User

(因為最近寫個小工具,在手機平台上需要用到,所以整理了大部分相關文檔,希望對讀者有用,若有筆誤請來信告訴謝謝:silentbalanceyh@126.com)

1.Java記憶體回收行程概述

  Java2平台裡面引入了java.lang.ref包,這個包中的類可以讓我們引用對象,而是的這些對象不用停留在記憶體中。不僅僅如此,這些類和Java本身的記憶體回收行程還存在一定的互動。我們平時開發的時候有時候會用到System.gc()方法,而Java裡面GC的基本工作原理又是如何呢?當然使用Java引用類主要目的是為了和Java記憶體回收行程進行有限的互動,從而全面提升記憶體的管理效率,這種情況在嵌入式系統和即時系統以及一些對記憶體要求嚴格的系統裡面比較湊效。

1)GC的基本原理

  Java的記憶體管理主要指的是對記憶體中的對象的管理,包括針對這些對象進行記憶體的分配和釋放等各種操作,學過Java的人都瞭解Java本身的記憶體模型,對於一個Java的對象而言,儲存主要分為兩種,一種是記憶體堆(Heap),記憶體堆是無序的,主要用來存放建立的Java對象;一種是記憶體棧(Stack),主要用來存放Java引用,然後在管理過程使用Java引用指向Java對象。而GC就是負責在對象“不可達”的時候將對象回收,常見的語句例如:

Object o = null;

  而CG本身是如何工作的呢?當系統在建立對象的時候,即當我們使用new關鍵字建立一個對象的時候,GC就開始監控對象的地址、大小以及使用狀態。一般情況下,Java的GC機制都有特定的回收演算法,GC通常會使用有向圖的方式來記錄隊中的所有對象,通過此種方式確定哪些對象是“可達的”,而哪些是“不可達的”。當GC判斷一些對象不可達的時候,GC就有責任回收相關記憶體空間。但是,因為平台的不同,往往在調用System.gc()的時候,存在太多不確定性,可以這樣認為,當程式調用了System.gc()過後,僅僅是程式向JVM發送了請求,至於JVM究竟在什麼時候進行記憶體回收,不同的平台不一樣。(*:需要解決的誤區是不要覺得System.gc()調用了過後,記憶體回收行程一定會對系統記憶體進行回收,系統回收相關記憶體取決於平台和系統。)

2)增量方式GC(IncrementalGC)

  GC在JVM中通常是啟動了一個新的進程或者一組新的進程,它本身和Java使用者程式一樣需要佔用heap空間,運行時也佔用CPU。設計GC的時候,必須要在停頓時間和回收率之間進行權衡,原因在於它本身佔用了Heap,如果GC已耗用時間太長,使用者就會感覺到Java程式本身會有一定的停頓,如果已耗用時間太短,則有很多記憶體沒有回收,使得程式裡面建立的Java對象佔用了大量的記憶體。增量方式的GC就是通過一定的回收演算法,把一個長時間的中斷,分成很多小的中斷,通過這種方式減少GC對程式本身的影響。其實增量方式整體效能比不上普通的高,但是能夠減少停頓時間,改善使用者的使用者體驗。當然除了這樣的方式,GC整體的方式為:

  引用計數法(Reference Counting Collector) ;

  Tracing演算法(Tracing Collector);

  Compacting演算法(Compacting Collector) ;

  Copying演算法(Coping Collector) ;

  Generation演算法(Generational Collector) ;

  Adaptive演算法(Adaptive Collector) ;

  至於相關的演算法可以去查閱相關的文檔

2.Java中的對象引用分類

  Java中的對象引用主要有以下幾種類型:

  1)強可及對象(strongly reachable):

  可以通過強引用訪問的對象,一般來說,我們平時寫代碼的方式都是使用的強引用對象,比如下邊的程式碼片段:

  StringBuilder builder= new StringBuilder();

  上邊代碼部分引用obj這個引用將引用記憶體堆中的一個對象,這種情況下,只要obj的引用存在,記憶體回收行程就永遠不會釋放該對象的儲存空間。這種對象我們又成為強引用(Strong references),這種強引用方式就是Java語言的原生的Java引用,我們幾乎每天編程的時候都用到。上邊代碼JVM儲存了一個StringBuilder類型的對象的強引用在變數builder呢。強引用和GC的互動是這樣的,如果一個對象通過強引用可達或者通過強引用鏈可達的話這種對象就成為強可及對象,這種情況下的對象記憶體回收行程不予理睬。如果我們開發過程不需要垃圾回器回收該對象,就直接將該對象賦為前引用。

  2)軟可及對象(softly reachable):

  不通過強引用訪問的對象,即不是強可及對象,但是可以通過軟引用訪問的對象就成為軟可及對象,軟可及對象就需要使用類SoftReference(java.lang.ref.SoftReference)。此種類型的引用主要用於記憶體比較敏感的快取,而且此種引用還是具有較強的引用功能,當記憶體不夠的時候GC會回收這類記憶體,因此如果記憶體充足的時候,這種引用通常不會被回收的。不僅僅如此,這種引用對象在JVM裡面保證在拋出OutOfMemory異常之前,設定成為null。通俗地講,這種類型的引用保證在JVM記憶體不足的時候全部被清楚,但是有個關鍵在於:垃圾收集器在運行時是否釋放軟可及對象是不確定的,而且使用記憶體回收演算法並不能保證一次性尋找到所有的軟可及對象。當記憶體回收行程每次啟動並執行時候都可以隨意釋放不是強可及對象佔用的記憶體,如果記憶體回收行程找到了軟可及對象過後,可能會進行以下操作:

【1】將SoftReference對象的referent網域設定成為null,從而使該對象不再引用heap對象。

【2】SoftReference引用過的記憶體堆上的對象一律被生命為finalizable。

【3】當記憶體堆上的對象finalize()方法被運行而且該對象佔用的記憶體被釋放,SoftReference對象就會被添加到它的ReferenceQueue,前提條件是ReferenceQueue本身是存在的。

  既然Java裡面存在這樣的對象,那麼我們在編寫代碼的時候如何建立這樣的對象呢?建立步驟如下:

  先建立一個對象,並使用普通引用方式【強引用】,然後再建立一個SoftReference來引用該對象,最後將普通引用設定為null,通過這樣的方式,這個對象就僅僅保留了一個SoftReference引用,同時這種情況我們所建立的對象就是SoftReference對象。一般情況下,我們可以使用該引用來完成Cache功能,就是前邊說的用於快取,保證最大限度使用記憶體而不會引起記憶體流失的情況。下邊的程式碼片段:

  public static void main(String args[])

  {

    //建立一個強可及對象

    A a = new A();

    //建立這個對象的軟引用SoftReference

    SoftReference sr = new SoftReference(a);

    //將強引用設定為空白,以遍記憶體回收行程回收強引用

    a = null;

    //下次使用該對象的操作

    if( sr != null ){

      a = (A)sr.get();

    }else{

      //這種情況就是由於記憶體過低,已經將軟引用釋放了,因此需要重新裝載一次

      a = new A();

      sr = new SoftReference(a);

    }

  }

  軟引用技術使得Java系統可以更好地管理記憶體,保持系統穩定,防止記憶體流失,避免系統崩潰,因此在處理一些記憶體佔用大而且生命週期長使用不頻繁的對象可以使用該技術。

  3)弱可及對象(weakly reachable):

  不是強可及對象同樣也不是軟可及對象,僅僅通過弱引用WeakReference(java.lang.ref.WeakReference)訪問的對象,這種對象的用途在於正常化映射(canonicalized mapping),對於生存周期相對比較長而且重新建立的時候開銷少的對象,弱引用也比較有用,和軟引用對象不同的是,記憶體回收行程如果碰到了弱可及對象,將釋放WeakReference對象的記憶體,但是記憶體回收行程需要運行很多次才能夠找到弱可及對象。弱引用對象在使用的時候,可以配合ReferenceQueue類使用,如果弱引用被回收,JVM就會把這個弱引用加入到相關的引用隊列中去。最簡單的弱引用方法如以下代碼:

  WeakReference weakWidget = new WeakReference(classA);

  在上邊代碼裡面,當我們使用weakWidget.get()來擷取classA的時候,由於弱引用本身是無法阻止記憶體回收的,所以我們也許會拿到一個null為返回。【*:這裡提供一個小技巧,如果我們希望取得某個對象的資訊,但是又不影響該對象的記憶體回收過程,我們就可以使用WeakReference來記住該對象,一般我們在開發調試器和最佳化器的時候使用這個是很好的一個手段。】

  如果上邊的代碼部分,我們通過weakWidget.get()返回的是null就證明該對象已經被記憶體回收行程回收了,而這種情況下弱引用對象就失去了使用價值,GC就會定義為需要進行清除工作。這種情況下弱引用無法引用任何對象,所以在JVM裡面就成為了一個死引用,這就是為什麼我們有時候需要通過ReferenceQueue類來配合使用的原因,使用了ReferenceQueue過後,就使得我們更加容易監視該引用的對象,如果我們通過一ReferenceQueue類來構造一個若引用,當若引用的對象已經被回收的時候,系統將自動使用對象引用隊列來代替對象引用,而且我們可以通過ReferenceQueue類的運行來決定是否真正要從記憶體回收行程裡面將該死引用(Dead Reference)清除。

  弱引用程式碼片段:

  //建立普通引用對象

  MyObject object = new MyObject();

  //建立一個引用隊列

  ReferenceQueue rq = new ReferenceQueue();

  //使用引用隊列建立MyObject的弱引用

  WeakReference wr = new WeakReference(object,rq);

  這裡提供兩個實在的情境來描述弱引用的相關用法:

  (1)你想給對象附加一些資訊,於是你用一個 Hashtable 把對象和附加資訊關聯起來。你不停的把對象和附加資訊放入 Hashtable 中,但是當對象用完的時候,你不得不把對象再從 Hashtable 中移除,否則它佔用的記憶體變不會釋放。萬一你忘記了,那麼沒有從 Hashtable 中移除的對象也可以算作是記憶體流失。理想的狀況應該是當對象用完時,Hashtable 中的對象會自動被垃圾收集器回收,不然你就是在做記憶體回收的工作。

  (2)你想實現一個圖片緩衝,因為載入圖片的開銷比較大。你將圖片對象的引用放入這個緩衝,以便以後能夠重新使用這個對象。但是你必須決定緩衝中的哪些圖片不再需要了,從而將引用從緩衝中移除。不管你使用什麼管理緩衝的演算法,你實際上都在處理垃圾收集的工作,更簡單的辦法(除非你有特殊的需求,這也應該是最好的辦法)是讓垃圾收集器來處理,由它來決定回收哪個對象。

  當Java回收器遇到了弱引用的時候有可能會執行以下操作:

【1】將WeakReference對象的referent網域設定成為null,從而使該對象不再引用heap對象。

【2】WeakReference引用過的記憶體堆上的對象一律被生命為finalizable。

【3】當記憶體堆上的對象finalize()方法被運行而且該對象佔用的記憶體被釋放,WeakReference對象就會被添加到它的ReferenceQueue,前提條件是ReferenceQueue本身是存在的。

  4)清除:

  當引用對象的referent網域設定為null,並且引用類在記憶體堆中引用的對象聲明為可結束的時候,該對象就可以清除,清除不做過多的講述

  5)虛可及對象(phantomly reachable):

  不是強可及對象,也不是軟可及對象,同樣不是弱可及對象,之所以把虛可及對象放到最後來講,主要也是因為它的特殊性,有時候我們又稱之為“幽靈對象”,已經結束的,可以通過虛引用來訪問該對象。我們使用類PhantomReference(java.lang.ref.PhantomReference)來訪問,這個類只能用於跟蹤被引用對象進行的收集,同樣的,可以用於執行per-mortern清除操作。PhantomReference必須與ReferenceQueue類一起使用。需要使用ReferenceQueue是因為它能夠充當通知機制,當垃圾收集器確定了某個對象是虛可及對象的時候,PhantomReference對象就被放在了它的ReferenceQueue上,這就是一個通知,表明PhantomReference引用的對象已經結束,可以收集了,一般情況下我們剛好在對象記憶體在回收之前採取該行為。這種引用不同於弱引用和軟引用,這種方式通過get()擷取到的對象總是返回null,僅僅當這些對象在ReferenceQueue隊列裡面的時候,我們可以知道它所引用的哪些對對象是死引用(Dead Reference)。而這種引用和弱引用的區別在於:

  弱引用(WeakReference)是在對象不可達的時候儘快進入ReferenceQueue隊列的,在finalization方法執行和記憶體回收之前是確實會發生的,理論上這類對象是不正確的對象,但是WeakReference對象可以繼續保持Dead狀態,

  虛引用(PhantomReference)是在對象確實已經從實體記憶體中移除過後才進入的ReferenceQueue隊列,而且get()方法會一直返回null

  當記憶體回收行程遇到了虛引用的時候將有可能執行以下操作:

【1】PhantomReference引用過的heap對象聲明為finalizable;

【2】虛引用在堆對象釋放之前就添加到了它的ReferenceQueue裡面,這種情況使得我們可以在堆對象被回收之前採取操作(*:再次提醒,PhantomReference對象必須經過關聯的ReferenceQueue來建立,就是說必須和ReferenceQueue類配合操作)

  看似沒有用處的虛引用,有什麼用途呢?

1.首先,我們可以通過虛引用知道對象究竟什麼時候真正從記憶體裡面移除的,而且這也是唯一的途徑。

2.虛引用避過了finalize()方法,因為對於此方法的執行而言,虛引用真正引用到的對象是異常對象,若在該方法內要使用對象只能重建。一般情況記憶體回收行程會輪詢兩次,一次標記為finalization,第二次進行真實的回收,而往往標記工作不能即時進行,或者記憶體回收其會等待一個對象去標記finalization。這種情況很有可能引起MemoryOut,而使用虛引用這種情況就會完全避免。因為虛引用在引用對象的過程不會去使得這個對象由Dead複活,而且這種對象是可以在回收周期進行回收的。

  在JVM內部,虛引用比起使用finalize()方法更加安全一點而且更加有效。而finaliaze()方法回收在虛擬機器裡面實現起來相對簡單,而且也可以處理大部分工作,所以我們仍然使用這種方式來進行對象回收的掃尾操作,但是有了虛引用過後我們可以選擇是否手動操作該對象使得程式更加高效完美。

3.Java裡面對象的生命週期

  在JVMRunspace裡面,對象整個聲明周期大致分為以下幾個階段:

  建立階段(Creating)->應用階段(Using)->不可視階段(Invisible)->不可達階段(Unreachable)->可收集階段(Collected)->終結階段(Finalized)->釋放階段(Free)

  【1】建立階段:

  建立過程需要經過其中幾步:

  為對象分配記憶體空間

  開始構造對象

  遞迴調用超類的構造方法

  進行對象執行個體初始化和變數初始化

  執行構造方法體

  【2】應用階段特徵:

  系統至少維護著對象的一個強引用(Strong Reference)

  所有該對象的引用全部是強引用,除非我們顯示聲明了軟引用、弱引用或者虛引用

  【3】不可是視階段:

  不可視階段就是我們在地區代碼中不可以再引用它,就是強引用已經消失,一般情況我們把這個時候的對象設定為null,其主要目的是讓JVM發現它,並且可以及時回收該對象所佔用資源

  【4】不可到達階段:

  不可達階段的對象,在虛擬機器所管理的對象引用根集合中再也找不到直接或間接的強引用,這些對象通常是指所有線程棧中的臨時變數以及相關引用,這種對象都是要預備回收的對象,但是這時候不能被GC直接回收。

  【5】可收集階段、終結階段、釋放階段:

  對象生命週期最後一個階段,這種階段的對象可能處於三種狀態:

  記憶體回收行程發現對象已經不可達

  finalize方法已經被執行

     對象已經被重用

4.總結:

  整個Java裡面有關對象引用的內容我就整理了這些,基本上涵蓋了大部分內容,如果有筆誤希望閱讀的人來信告知:silentbalanceyh@126.com。

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/silentbalanceyh/archive/2009/08/21/4468368.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.