隨筆-17  評(píng)論-0  文章-4  trackbacks-0

          垃圾回收與強(qiáng)引用,軟引用,弱引用,幻引用的關(guān)系(一)

          Java 2 平臺(tái)引入了 java.lang.ref 包,其中包括的類可以讓您引用對(duì)象,而不將它們留在內(nèi)存中。這些類還提供了與垃圾收集器(garbage collector)之間有限的交互。Peter Haggar 在本文中分析了 SoftReference、WeakReference 和 PhantomReference 類的功能和行為,并就這些類的使用給出了一些編程風(fēng)格上的建議。
          當(dāng)在 Java 2 平臺(tái)中首次引入 java.lang.ref 包(其中包含 SoftReference、WeakReference 和 PhantomReference 類)時(shí),它的實(shí)用性顯然被過分夸大了。它包含的類可能是有用的,但這些類具有的某些局限性會(huì)使它們顯得不是很有吸引力,而且其應(yīng)用程序也將特別局限于解決一類特定的問題。

          垃圾收集概述
          引用類的主要功能就是能夠引用仍可以被垃圾收集器回收的對(duì)象。在引入引用類之前,我們只能使用強(qiáng)引用(strong reference)。舉例來說,下面一行代碼顯示的就是強(qiáng)引用 obj:


          Object obj = new Object();




          obj 這個(gè)引用將引用堆中存儲(chǔ)的一個(gè)對(duì)象。只要 obj 引用還存在,垃圾收集器就永遠(yuǎn)不會(huì)釋放用來容納該對(duì)象的存儲(chǔ)空間。

          當(dāng) obj 超出范圍或被顯式地指定為 null 時(shí),垃圾收集器就認(rèn)為沒有對(duì)這個(gè)對(duì)象的其它引用,也就可以收集它了。然而您還需要注意一個(gè)重要的細(xì)節(jié):僅憑對(duì)象可以被收集并不意味著垃圾收集器的一次指定運(yùn)行就能夠回收它。由于各種垃圾收集算法有所不同,某些算法會(huì)更頻繁地分析生存期較短的對(duì)象,而不是較老、生存期較長(zhǎng)的對(duì)象。因此,一個(gè)可供收集的對(duì)象可能永遠(yuǎn)也不會(huì)被回收。如果程序在垃圾收集器釋放對(duì)象之前結(jié)束,這種情況就可能會(huì)出現(xiàn)。因此,概括地說,您永遠(yuǎn)無法保證可供收集的對(duì)象總是會(huì)被垃圾收集器收集。

          這些信息對(duì)于您分析引用類是很重要的。由于垃圾收集有著特定的性質(zhì),所以引用類實(shí)際上可能沒有您原來想像的那么有用,盡管如此,它們對(duì)于特定問題來說還是很有用的類。軟引用(soft reference)、弱引用(weak reference)和虛引用(phantom reference)對(duì)象提供了三種不同的方式來在不妨礙收集的情況下引用堆對(duì)象。每種引用對(duì)象都有不同的行為,而且它們與垃圾收集器之間的交互也有所不同。此外,這幾個(gè)新的引用類都表現(xiàn)出比典型的強(qiáng)引用“更弱”的引用形式。而且,內(nèi)存中的一個(gè)對(duì)象可以被多個(gè)引用(可以是強(qiáng)引用、軟引用、弱引用或虛引用)引用。在進(jìn)一步往下討論之前,讓我們來看看一些術(shù)語:

          強(qiáng)可及對(duì)象(strongly reachable):可以通過強(qiáng)引用訪問的對(duì)象。


          軟可及對(duì)象(softly reachable):不是強(qiáng)可及對(duì)象,并且能夠通過軟引用訪問的對(duì)象。


          弱可及對(duì)象(weakly reachable):不是強(qiáng)可及對(duì)象也不是軟可及對(duì)象,并且能夠通過弱引用訪問的對(duì)象。


          虛可及對(duì)象(phantomly reachable):不是強(qiáng)可及對(duì)象、軟可及對(duì)象,也不是弱可及對(duì)象,已經(jīng)結(jié)束的,可以通過虛引用訪問的對(duì)象。


          清除:將引用對(duì)象的 referent 域設(shè)置為 null,并將引用類在堆中引用的對(duì)象聲明為可結(jié)束的。
          SoftReference 類
          SoftReference 類的一個(gè)典型用途就是用于內(nèi)存敏感的高速緩存。SoftReference 的原理是:在保持對(duì)對(duì)象的引用時(shí)保證在 JVM 報(bào)告內(nèi)存不足情況之前將清除所有的軟引用。關(guān)鍵之處在于,垃圾收集器在運(yùn)行時(shí)可能會(huì)(也可能不會(huì))釋放軟可及對(duì)象。對(duì)象是否被釋放取決于垃圾收集器的算法以及垃圾收集器運(yùn)行時(shí)可用的內(nèi)存數(shù)量。 

          WeakReference 類
          WeakReference 類的一個(gè)典型用途就是規(guī)范化映射(canonicalized mapping)。另外,對(duì)于那些生存期相對(duì)較長(zhǎng)而且重新創(chuàng)建的開銷也不高的對(duì)象來說,弱引用也比較有用。關(guān)鍵之處在于,垃圾收集器運(yùn)行時(shí)如果碰到了弱可及對(duì)象,將釋放 WeakReference 引用的對(duì)象。然而,請(qǐng)注意,垃圾收集器可能要運(yùn)行多次才能找到并釋放弱可及對(duì)象。

          PhantomReference 類
          PhantomReference 類只能用于跟蹤對(duì)被引用對(duì)象即將進(jìn)行的收集。同樣,它還能用于執(zhí)行 pre-mortem 清除操作。PhantomReference 必須與 ReferenceQueue 類一起使用。需要 ReferenceQueue 是因?yàn)樗軌虺洚?dāng)通知機(jī)制。當(dāng)垃圾收集器確定了某個(gè)對(duì)象是虛可及對(duì)象時(shí),PhantomReference 對(duì)象就被放在它的 ReferenceQueue 上。將 PhantomReference 對(duì)象放在 ReferenceQueue 上也就是一個(gè)通知,表明 PhantomReference 對(duì)象引用的對(duì)象已經(jīng)結(jié)束,可供收集了。這使您能夠剛好在對(duì)象占用的內(nèi)存被回收之前采取行動(dòng)。  

          垃圾收集器和引用交互
          垃圾收集器每次運(yùn)行時(shí)都可以隨意地釋放不再是強(qiáng)可及的對(duì)象占用的內(nèi)存。如果垃圾收集器發(fā)現(xiàn)了軟可及對(duì)象,就會(huì)出現(xiàn)下列情況:

          SoftReference 對(duì)象的 referent 域被設(shè)置為 null,從而使該對(duì)象不再引用 heap 對(duì)象。


          SoftReference 引用過的 heap 對(duì)象被聲明為 finalizable。


          當(dāng) heap 對(duì)象的 finalize() 方法被運(yùn)行而且該對(duì)象占用的內(nèi)存被釋放,SoftReference 對(duì)象就被添加到它的 ReferenceQueue(如果后者存在的話)。
          如果垃圾收集器發(fā)現(xiàn)了弱可及對(duì)象,就會(huì)出現(xiàn)下列情況:

          WeakReference 對(duì)象的 referent 域被設(shè)置為 null,從而使該對(duì)象不再引用 heap 對(duì)象。


          WeakReference 引用過的 heap 對(duì)象被聲明為 finalizable。


          當(dāng) heap 對(duì)象的 finalize() 方法被運(yùn)行而且該對(duì)象占用的內(nèi)存被釋放時(shí),WeakReference 對(duì)象就被添加到它的 ReferenceQueue(如果后者存在的話)。
          如果垃圾收集器發(fā)現(xiàn)了虛可及對(duì)象,就會(huì)出現(xiàn)下列情況:

          PhantomReference 引用過的 heap 對(duì)象被聲明為 finalizable。


          與軟引用和弱引用有所不同,PhantomReference 在堆對(duì)象被釋放之前就被添加到它的 ReferenceQueue。(請(qǐng)記住,所有的 PhantomReference 對(duì)象都必須用經(jīng)過關(guān)聯(lián)的 ReferenceQueue 來創(chuàng)建。)這使您能夠在堆對(duì)象被回收之前采取行動(dòng)。
          請(qǐng)考慮清單 1 中的代碼。

          清單 1. 使用 WeakReference 及 ReferenceQueue 的示例代碼
          //Create a strong reference to an object
          MyObject obj = new MyObject(); //1

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue(); //2

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq); //3



          行 //1 創(chuàng)建 MyObject 對(duì)象,而行 //2 則創(chuàng)建 ReferenceQueue 對(duì)象。行 //3 創(chuàng)建引用其引用對(duì)象 MyObject 的 WeakReference 對(duì)象,還創(chuàng)建它的 ReferenceQueue。請(qǐng)注意,每個(gè)對(duì)象引用(obj、rq 及 wr)都是強(qiáng)引用。要利用這些引用類,您必須取消對(duì) MyObject 對(duì)象的強(qiáng)引用,方法是將 obj 設(shè)置為 null。前面說過,如果不這樣做,對(duì)象 MyObject 永遠(yuǎn)都不會(huì)被回收,引用類的任何優(yōu)點(diǎn)都會(huì)被削弱。

          每個(gè)引用類都有一個(gè) get() 方法,而 ReferenceQueue 類有一個(gè) poll() 方法。get() 方法返回對(duì)被引用對(duì)象的引用。在 PhantomReference 上調(diào)用 get() 總是會(huì)返回 null。這是因?yàn)?nbsp;PhantomReference 只用于跟蹤收集。poll() 方法返回已被添加到隊(duì)列中的引用對(duì)象,如果隊(duì)列中沒有任何對(duì)象,它就返回 null。因此,執(zhí)行清單 1 之后再調(diào)用 get() 和 poll() 的結(jié)果可能是:


          wr.get(); //returns reference to MyObject
          rq.poll(); //returns null




          現(xiàn)在我們假定垃圾收集器開始運(yùn)行。由于 MyObject 對(duì)象沒有被釋放,所以 get() 和 poll() 方法將返回同樣的值;obj 仍然保持對(duì)該對(duì)象進(jìn)行強(qiáng)引用。實(shí)際上,對(duì)象布局還是沒有改變,和圖 1 所示的差不多。然而,請(qǐng)考慮下面的代碼:


          obj = null;
          System.gc(); //run the collector







          現(xiàn)在,調(diào)用 get() 和 poll() 將產(chǎn)生與前面不同的結(jié)果:


          wr.get(); //returns null
          rq.poll(); //returns a reference to the WeakReference object




          這種情況表明,MyObject 對(duì)象(對(duì)它的引用原來是由 WeakReference 對(duì)象進(jìn)行的)不再可用。這意味著垃圾收集器釋放了 MyObject 占用的內(nèi)存,從而使 WeakReference 對(duì)象可以被放在它的 ReferenceQueue 上。這樣,您就可以知道當(dāng) WeakReference 或 SoftReference 類的 get() 方法返回 null 時(shí),就有一個(gè)對(duì)象被聲明為 finalizable,而且可能(不過不一定)被收集。只有當(dāng) heap 對(duì)象完全結(jié)束而且其內(nèi)存被回收后,WeakReference 或 SoftReference 才會(huì)被放到與其關(guān)聯(lián)的 ReferenceQueue 上。清單 2 顯示了一個(gè)完整的可運(yùn)行程序,它展示了這些原理中的一部分。這段代碼本身就頗具說明性,它含有很多注釋和打印語句,可以幫助您理解。

          清單 2. 展示引用類原理的完整程序
          import java.lang.ref.*;
          class MyObject
          {
          protected void finalize() throws Throwable
          {
          System.out.println("In finalize method for this object: " +
          this);
          }
          }

          class ReferenceUsage
          {
          public static void main(String args[])
          {
          hold();
          release();
          }

          public static void hold()
          {
          System.out.println("Example of incorrectly holding a strong " +
          "reference");
          //Create an object
          MyObject obj = new MyObject();
          System.out.println("object is " + obj);

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue();

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq);

          System.out.println("The weak reference is " + wr);

          //Check to see if it´s on the ref queue yet
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());

          System.out.println("Calling GC");
          System.gc();
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());
          }

          public static void release()
          {
          System.out.println("");
          System.out.println("Example of correctly releasing a strong " +
          "reference");
          //Create an object
          MyObject obj = new MyObject();
          System.out.println("object is " + obj);

          //Create a reference queue
          ReferenceQueue rq = new ReferenceQueue();

          //Create a weakReference to obj and associate our reference queue
          WeakReference wr = new WeakReference(obj, rq);

          System.out.println("The weak reference is " + wr);

          //Check to see if it´s on the ref queue yet
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());

          System.out.println("Set the obj reference to null and call GC");
          obj = null;
          System.gc();
          System.out.println("Polling the reference queue returns " +
          rq.poll());
          System.out.println("Getting the referent from the " +
          "weak reference returns " + wr.get());
          }
          }




          用途和風(fēng)格
          這些類背后的原理就是避免在應(yīng)用程序執(zhí)行期間將對(duì)象留在內(nèi)存中。相反,您以軟引用、弱引用或虛引用的方式引用對(duì)象,這樣垃圾收集器就能夠隨意地釋放對(duì)象。當(dāng)您希望盡可能減小應(yīng)用程序在其生命周期中使用的堆內(nèi)存大小時(shí),這種用途就很有好處。您必須記住,要使用這些類,您就不能保留對(duì)對(duì)象的強(qiáng)引用。如果您這么做了,那就會(huì)浪費(fèi)這些類所提供的任何好處。

          另外,您必須使用正確的編程風(fēng)格以檢查收集器在使用對(duì)象之前是否已經(jīng)回收了它,如果已經(jīng)回收了,您首先必須重新創(chuàng)建該對(duì)象。這個(gè)過程可以用不同的編程風(fēng)格來完成。選擇錯(cuò)誤的風(fēng)格會(huì)導(dǎo)致出問題。請(qǐng)考慮清單 3 中從 WeakReference 檢索被引用對(duì)象的代碼風(fēng)格:

          清單 3. 檢索被引用對(duì)象的風(fēng)格
          obj = wr.get();
          if (obj == null)
          {
          wr = new WeakReference(recreateIt()); //1
          obj = wr.get(); //2
          }
          //code that works with obj




          研究了這段代碼之后,請(qǐng)看看清單 4 中從 WeakReference 檢索被引用對(duì)象的另一種代碼風(fēng)格:

          清單 4. 檢索被引用對(duì)象的另一種風(fēng)格
          obj = wr.get();
          if (obj == null)
          {
          obj = recreateIt(); //1
          wr = new WeakReference(obj); //2
          }
          //code that works with obj




          請(qǐng)比較這兩種風(fēng)格,看看您能否確定哪種風(fēng)格一定可行,哪一種不一定可行。清單 3 中體現(xiàn)出的風(fēng)格不一定在所有情況下都可行,但清單 4 的風(fēng)格就可以。清單 3 中的風(fēng)格不夠好的原因在于,if 塊的主體結(jié)束之后 obj 不一定是非空值。請(qǐng)考慮一下,如果垃圾收集器在清單 3 的行 //1 之后但在行 //2 執(zhí)行之前運(yùn)行會(huì)怎樣。recreateIt() 方法將重新創(chuàng)建該對(duì)象,但它會(huì)被 WeakReference 引用,而不是強(qiáng)引用。因此,如果收集器在行 //2 在重新創(chuàng)建的對(duì)象上施加一個(gè)強(qiáng)引用之前運(yùn)行,對(duì)象就會(huì)丟失,wr.get() 則返回 null。

          清單 4 不會(huì)出現(xiàn)這種問題,因?yàn)樾?nbsp;//1 重新創(chuàng)建了對(duì)象并為其指定了一個(gè)強(qiáng)引用。因此,如果垃圾收集器在該行之后(但在行 //2 之前)運(yùn)行,該對(duì)象就不會(huì)被回收。然后,行 //2 將創(chuàng)建對(duì) obj 的 WeakReference。在使用這個(gè) if 塊之后的 obj 之后,您應(yīng)該將 obj 設(shè)置為 null,從而讓垃圾收集器能夠回收這個(gè)對(duì)象以充分利用弱引用。清單 5 顯示了一個(gè)完整的程序,它將展示剛才我們描述的風(fēng)格之間的差異。(要運(yùn)行該程序,其運(yùn)行目錄中必須有一個(gè)“temp.fil”文件。

          清單 5. 展示正確的和不正確的編程風(fēng)格的完整程序。
          import java.io.*;
          import java.lang.ref.*;

          class ReferenceIdiom
          {
          public static void main(String args[]) throws FileNotFoundException
          {
          broken();
          correct();
          }

          public static FileReader recreateIt() throws FileNotFoundException
          {
          return new FileReader("temp.fil");
          }

          public static void broken() throws FileNotFoundException
          {
          System.out.println("Executing method broken");
          FileReader obj = recreateIt();
          WeakReference wr = new WeakReference(obj);

          System.out.println("wr refers to object " + wr.get());

          System.out.println("Now, clear the reference and run GC");
          //Clear the strong reference, then run GC to collect obj.
          obj = null;
          System.gc();

          System.out.println("wr refers to object " + wr.get());

          //Now see if obj was collected and recreate it if it was.
          obj = (FileReader)wr.get();
          if (obj == null)
          {
          System.out.println("Now, recreate the object and wrap it
          in a WeakReference");
          wr = new WeakReference(recreateIt());
          System.gc(); //FileReader object is NOT pinned...there is no
          //strong reference to it. Therefore, the next
          //line can return null.
          obj = (FileReader)wr.get();
          }
          System.out.println("wr refers to object " + wr.get());
          }

          public static void correct() throws FileNotFoundException
          {
          System.out.println("");
          System.out.println("Executing method correct");
          FileReader obj = recreateIt();
          WeakReference wr = new WeakReference(obj);

          System.out.println("wr refers to object " + wr.get());

          System.out.println("Now, clear the reference and run GC");
          //Clear the strong reference, then run GC to collect obj
          obj = null;
          System.gc();

          System.out.println("wr refers to object " + wr.get());

          //Now see if obj was collected and recreate it if it was.
          obj = (FileReader)wr.get();
          if (obj == null)
          {
          System.out.println("Now, recreate the object and wrap it
          in a WeakReference");
          obj = recreateIt();
          System.gc(); //FileReader is pinned, this will not affect
          //anything.
          wr = new WeakReference(obj);
          }
          System.out.println("wr refers to object " + wr.get());
          }
          }




          總結(jié)
          如果使用得當(dāng),引用類還是很有用的。然而,由于它們所依賴的垃圾收集器行為有時(shí)候無法預(yù)知,所以其實(shí)用性就會(huì)受到影響。能否有效地使用它們還取決于是否應(yīng)用了正確的編程風(fēng)格;關(guān)鍵在于您要理解這些類是如何實(shí)現(xiàn)的以及如何對(duì)它們進(jìn)行編程。
          =================================================================================

          Java 對(duì)象的狀態(tài)有:

              * 已創(chuàng)建(created)
              * 強(qiáng)可達(dá)(strong reachable)
              * 不可見(invisible)
              * 不可達(dá)(unreachable)
              * 已收集(collected)
              * 終化(finalized)
              * 已回收(deallocated) 

          Java對(duì)象生命周期的狀態(tài)轉(zhuǎn)換: {image:img=objectstatus.jpg|width=400} 引用對(duì)象
          三種新的引用類型:

              * 軟引用(soft reference)
              * 弱引用(weak reference)
              * 幻引用(phantom reference) 

          強(qiáng)可達(dá)(Strong Reachable)
          定義: ~An object is strong reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strong reachable by the thread that created it.~
          處于強(qiáng)可達(dá)狀態(tài)的對(duì)象, 在任何情況下都不會(huì)被回收掉. 軟可達(dá)(Softly Reachable)
          定義:~An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.~
          含義是:當(dāng)對(duì)象不處于強(qiáng)可達(dá)狀態(tài), 并且可以通過軟引用進(jìn)行訪問時(shí), 即處于軟可達(dá)狀態(tài).
          當(dāng)程序申請(qǐng)內(nèi)存的時(shí)候, 垃圾收集器會(huì)判斷是否開始回收處于軟可達(dá)狀態(tài)的對(duì)象, 如果決定回收某個(gè)對(duì)象, 那么垃圾收集器會(huì)清除所有指向該對(duì)象的軟引用, 如果任何處于其它軟可達(dá)狀態(tài)的對(duì)象可以通過強(qiáng)引用訪問該對(duì)象, 那么指向這些對(duì)象的軟引用也會(huì)被清除掉. 垃圾收集器在決定哪些軟可達(dá)狀態(tài)的對(duì)象被收集時(shí), 采用"最久未被使用"原則, 或稱"最不常使用"原則. 垃圾收集器也保證在OutOfMemeryError產(chǎn)生以前, 所有的軟引用都被清除.

              * 產(chǎn)生和使用一個(gè)軟引用 

          // createSoftReference sr = new SoftReference(new SomeObject());// getSomeObject o = (SomeObject) sf.get();// create in a reference queue;ReferenceQueue queue = new ReferenceQueue();SoftReference sr = new SoftReference(new SomeObject(), queue);

          弱可達(dá)(Weakly Reachable)
          定義:~An Object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference.~
          垃圾收集器會(huì)一次清除所有弱引用. 幻可達(dá)(Phantomly Reachable)
          定義:~An object is phantomly reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.~
          幻引用不能直接創(chuàng)建. 必須通過向引用隊(duì)列等級(jí)的途徑來創(chuàng)建:

          ReferenceQueue queue = new ReferenceQueue();PhantomReference pr = new PhantomReference (new SomeObject(), queue);

          你不可能從幻引用再次得到對(duì)象, pr.get()永遠(yuǎn)返回null. 另外, 必須調(diào)用Reference.clear()手工清除幻引用. All About ReferenceObjects No InterWiki reference defined in properties for Wiki called '[http'!)]
          Reference Objects No InterWiki reference defined in properties for Wiki called '[http'!)]
          Reference Objects and Garbage Collection No InterWiki reference defined in properties for Wiki called '[http'!)]
          \[Jike Thread\?Soft, Weak, and Phantom References|http://www-124.ibm.com/pipermail/jikesrvm-core/2003-May/000365.html]


          Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1492810
          posted on 2008-12-06 21:13 竹子 閱讀(488) 評(píng)論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 建始县| 河北区| 虞城县| 阿图什市| 石景山区| 依安县| 大化| 乳山市| 长子县| 开封县| 固阳县| 保康县| 礼泉县| 新和县| 江阴市| 永兴县| 比如县| 乐都县| 崇文区| 滦南县| 客服| 定西市| 泾川县| 龙陵县| 和平县| 花垣县| 吉林省| 平阳县| 星子县| 乌审旗| 巴马| 宁远县| 河西区| 武功县| 政和县| 门头沟区| 东安县| 西城区| 庆元县| 阆中市| 墨竹工卡县|