日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入探讨 java.lang.ref 包--转

發(fā)布時間:2025/4/5 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入探讨 java.lang.ref 包--转 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

概述

Java.lang.ref 是 Java 類庫中比較特殊的一個包,它提供了與 Java 垃圾回收器密切相關(guān)的引用類。這些引用類對象可以指向其它對象,但它們不同于一般的引用,因為它們的存在并不防礙 Java 垃圾回收器對它們所指向的對象進行回收。其好處就在于使者可以保持對使用對象的引用,同時 JVM 依然可以在內(nèi)存不夠用的時候?qū)κ褂脤ο筮M行回收。因此這個包在用來實現(xiàn)與緩存相關(guān)的應(yīng)用時特別有用。同時該包也提供了在對象的“可達(dá)”性發(fā)生改變時,進行提醒的機制。本文通過對該包進行由淺入深的介紹與分析,使讀者可以加深對該包的理解,從而更好地利用該包進行開發(fā)。

java.lang.ref 包的介紹

我們可以先來看一下 java.lang.ref 這個包的結(jié)構(gòu),如圖 1 所示

圖 1. java.lang.ref 包結(jié)構(gòu)

該包中各類的繼承關(guān)系如圖 2 所示

圖 2. java.lang.ref 包中類的繼承關(guān)系 :

Reference 是一個抽象類,而 SoftReference,WeakReference,PhantomReference 以及 FinalReference 都是繼承它的具體類。

接下來我們來分別介紹和分析強引用以及 java.lang.ref 包下各種虛引用的特性及用法。

StrongReference, SoftReference, WeakReference 以及 PhantomReference 的特性及用法

StrongReference:

我們都知道 JVM 中對象是被分配在堆(heap)上的,當(dāng)程序行動中不再有引用指向這個對象時,這個對象就可以被垃圾回收器所回收。這里所說的引用也就是我們一般意義上申明的對象類型的變量(如 String, Object, ArrayList 等),區(qū)別于原始數(shù)據(jù)類型的變量(如 int, short, long 等)也稱為強引用。

在了解虛引用之前,我們一般都是使用強引用來對對象進行引用。如:

清單 1. StrongReference usage
String tag = new String("T");

此處的 tag 引用就稱之為強引用。而強引用有以下特征:

  • 強引用可以直接訪問目標(biāo)對象。
  • 強引用所指向的對象在任何時候都不會被系統(tǒng)回收。
  • 強引用可能導(dǎo)致內(nèi)存泄漏。

我們要討論的這三種 Reference 較之于強引用而言都屬于“弱引用”,也就是他們所引用的對象只要沒有強引用,就會根據(jù)條件被 JVM 的垃圾回收器所回收,它們被回收的時機以及用法各不相同。下面分別來進行討論。

SoftReference:

SoftReference 在“弱引用”中屬于最強的引用。SoftReference 所指向的對象,當(dāng)沒有強引用指向它時,會在內(nèi)存中停留一段的時間,垃圾回收器會根據(jù) JVM 內(nèi)存的使用情況(內(nèi)存的緊缺程度)以及 SoftReference 的 get() 方法的調(diào)用情況來決定是否對其進行回收。(后面章節(jié)會用幾個實驗進行闡述)

具體使用一般是通過 SoftReference 的構(gòu)造方法,將需要用弱引用來指向的對象包裝起來。當(dāng)需要使用的時候,調(diào)用 SoftReference 的 get() 方法來獲取。當(dāng)對象未被回收時 SoftReference 的 get() 方法會返回該對象的強引用。如下:

清單 2. SoftReference usage
SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10)); System.out.println(bean.get());// “name:10” 引用類型取得目標(biāo)對象方式垃圾回收條件是否可能內(nèi)存泄漏
強引用直接調(diào)用不回收可能
軟引用 視內(nèi)存情況回收不可能
弱引用通過 get() 方法永遠(yuǎn)回收不可能
虛引用無法取得不回收可能

?

類型是否拋出異常示例代碼運行結(jié)果
StrongReference拋出異常見清單 6Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
SoftReference不拋異常,之前的引用自動清空并返回 null見清單 7null
WeakReference同上見清單 8null
PhantomReference拋出異常見清單 9Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

?

FinalReference 以及 Finzlizer

FinalReference 作為 java.lang.ref 里的一個不能被公開訪問的類,又起到了一個什么樣的作用呢?作為他的子類, Finalizer 又在垃圾回收機制里扮演了怎么樣的角色呢?

實際上,FinalReference 代表的正是 Java 中的強引用,如這樣的代碼 :

Bean bean = new Bean();

在虛擬機的實現(xiàn)過程中,實際采用了 FinalReference 類對其進行引用。而 Finalizer,除了作為一個實現(xiàn)類外,更是在虛擬機中實現(xiàn)一個 FinalizerThread,以使虛擬機能夠在所有的強引用被解除后實現(xiàn)內(nèi)存清理。

讓我們來看看 Finalizer 是如何工作的。首先,通過聲明 FinalizerThread,并將該線程實例化,設(shè)置為守護線程后,加入系統(tǒng)線程中去。

清單 11
static { ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); Thread finalizer = new FinalizerThread(tg); finalizer.setPriority(Thread.MAX_PRIORITY - 2); finalizer.setDaemon(true); finalizer.start(); }

在 GC 的過程中,當(dāng)一個強引用被釋放,由系統(tǒng)垃圾收集器標(biāo)記后的對象,會被加入 Finalizer 對象中的 ReferenceQueue 中去,并調(diào)用 Finalizer.runFinalizer() 來執(zhí)行對象的 finalize 方法。

清單 12
private void runFinalizer() { synchronized (this) { if (hasBeenFinalized()) return; remove(); } try { Object finalizee = this.get(); if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { invokeFinalizeMethod(finalizee); /* 注意,這里需要清空棧中包含該變量的的 slot, ** 從而來減少因為一個保守的 GC 實現(xiàn)所造成的變量未被回收的假象 */ finalizee = null; } } catch (Throwable x) { } super.clear(); }

注意,標(biāo)記處所調(diào)用的 invokeFinalizeMethod 為 native 方法,由于 finalize 方法在 Object 類中被聲明為 protected,這里必須采用 native 方法才能調(diào)用。隨后通過將本地強引用設(shè)置為空,以便使垃圾回收器清理內(nèi)存。

可以看到,通過這樣的方法,Java 將四種引用對象類型:軟引用 (SoftReference),弱引用 (WeakReference),強引用 (FinalReference),虛引用 (PhantomReference) 平等地對待,并在垃圾回收器中進行統(tǒng)一調(diào)度和管理。

不同 Java 虛擬機上的表現(xiàn)與分析

讓我們來回顧一下四種引用類型的表現(xiàn)以及在垃圾回收器回收清理內(nèi)存時的表現(xiàn) .

  • 軟引用 (SoftReference), 引用類型表現(xiàn)為當(dāng)內(nèi)存接近滿負(fù)荷 , 或?qū)ο笥?SoftReference.get() 方法的調(diào)用沒有發(fā)生一段時間后 , 垃圾回收器將會清理該對象 . 在運行對象的 finalize 方法前 , 會將軟引用對象加入 ReferenceQueue 中去 .
  • 弱引用 (WeakReference), 引用類型表現(xiàn)為當(dāng)系統(tǒng)垃圾回收器開始回收時 , 則立即會回收該對象的引用 . 與軟引用一樣 , 弱引用也會在運行對象的 finalize 方法之前將弱引用對象加入 ReferenceQueue.
  • 強引用 (FinalReference), 這是最常用的引用類型 . JVM 系統(tǒng)采用 Finalizer 來管理每個強引用對象 , 并將其被標(biāo)記要清理時加入 ReferenceQueue, 并逐一調(diào)用該對象的 finalize() 方法 .
  • 虛引用 (PhantomReference), 這是一個最虛幻的引用類型 . 無論是從哪里都無法再次返回被虛引用所引用的對象 . 虛引用在系統(tǒng)垃圾回收器開始回收對象時 , 將直接調(diào)用 finalize() 方法 , 但不會立即將其加入回收隊列 . 只有在真正對象被 GC 清除時 , 才會將其加入 Reference 隊列中去 .
  • 這里比較兩個比較典型的 JVM 環(huán)境,Oracle Java SE6 和 IBM JDK 6。采用了如下的測試代碼 :

    清單 13. 類 RefTestObj
    public class RefTestObj { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public int hashCode() { return super.hashCode(); } @Override public String toString() { return super.toString() + "[id=" + this.id + "]"; } @Override protected void finalize() { System.out.println("Object [" + this.hashCode() + "][id=" + this.id + "] come into finalize"); try { super.finalize(); } catch (Throwable e) { e.printStackTrace(); } } }
    清單 14. 類 RefMainThread
    import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; public class RefMainThread { public static void main(String[] args) { // 創(chuàng)建三種不同的引用類型所需對象RefTestObj softRef = new RefTestObj(); RefTestObj weakRef = new RefTestObj(); RefTestObj phanRef = new RefTestObj(); softRef.setId(1); weakRef.setId(2); phanRef.setId(3); ReferenceQueue<RefTestObj> softRefQueue = new ReferenceQueue<RefTestObj>(); ReferenceQueue<RefTestObj> weakRefQueue = new ReferenceQueue<RefTestObj>(); ReferenceQueue<RefTestObj> phanRefQueue = new ReferenceQueue<RefTestObj>(); SoftReference<RefTestObj> softRefObj = new SoftReference<RefTestObj>(softRef, softRefQueue); WeakReference<RefTestObj> weakRefObj = new WeakReference<RefTestObj>(weakRef, weakRefQueue); PhantomReference<RefTestObj> phanRefObj = new PhantomReference<RefTestObj>(phanRef, phanRefQueue); // 打印正常情況下三種對象引用print(softRefObj); print(weakRefObj); print(phanRefObj); // 將對象清空softRef = null; weakRef = null; phanRef = null; // 打印引用隊列及 get() 方法所能取到的對象自身if (softRefObj != null) { System.out.println("Soft Reference Object run get():" + softRefObj.get()); System.out.println("Check soft queue:" + softRefQueue.poll()); } if (weakRefObj != null) { System.out.println("Weak Reference Object run get():" + weakRefObj.get()); System.out.println("Check weak queue:" + weakRefQueue.poll()); } if (phanRefObj != null) { System.out.println("Phantom Reference Object run get():" + phanRefObj.get()); System.out.println("Check Phantom queue:" + phanRefQueue.poll()); } // 開始執(zhí)行垃圾回收System.gc(); System.runFinalization(); // 檢查隊列,是否已經(jīng)被加入隊列,是否還能取回對象if (softRefObj != null) { System.out.println("Soft Reference Object run get():" + softRefObj.get()); System.out.println("Check soft queue:" + softRefQueue.poll()); } if (weakRefObj != null) { System.out.println("Weak Reference Object run get():" + weakRefObj.get()); System.out.println("Check weak queue:" + weakRefQueue.poll()); } if (phanRefObj != null) { System.out.println("Phantom Reference Object run get():" + phanRefObj.get()); System.out.println("Check Phantom queue:" + phanRefQueue.poll()); } // 對于虛引用對象,在經(jīng)過多次 GC 之后, 才會加入到隊列中去Reference<? extends RefTestObj> mynewphan = null; int refCount = 1; while (mynewphan == null) { mynewphan = phanRefQueue.poll(); System.gc(); System.runFinalization(); if (mynewphan != null) { System.out.println("Check Phantom queue:" + mynewphan); System.out.println("Count for " + refCount + " times"); break; } refCount ++; } } public static void print(Reference<RefTestObj> ref) { RefTestObj obj = ref.get(); System.out.println("The Reference is " + ref.toString() + " and with object " + obj +" which is " + (obj == null ? "null" : "not null")); } }

    通過執(zhí)行 RefMainThread, 我們可以清晰地根據(jù)打印結(jié)果看到對象在內(nèi)存中被加入隊列 , 以及調(diào)用 finalize 方法的順序及過程 .

    為了測試不同的 JVM 環(huán)境并消除其他因素的印象 , 本例采用的背景環(huán)境均為 Windows2003 下的 32bit JVM.

    首先采用了環(huán)境為 Oracle Java SE 6 update 23 進行測試 , 結(jié)果如下 :

    清單 15. Oracle Java SE 6 update 23 下測試結(jié)果
    The Reference is java.lang.ref.SoftReference@c17164 and with object RefTestObj@1fb8ee3[id=1] which is not null The Reference is java.lang.ref.WeakReference@61de33 and with object RefTestObj@14318bb[id=2] which is not null The Reference is java.lang.ref.PhantomReference@ca0b6 and with object null which is null Soft Reference Object run get():RefTestObj@1fb8ee3[id=1] Check soft queue:null Weak Reference Object run get():RefTestObj@14318bb[id=2] Check weak queue:null Phantom Reference Object run get():null Check Phantom queue:null Object [27744459][id=3] come into finalize Object [21174459][id=2] come into finalize Soft Reference Object run get():RefTestObj@1fb8ee3[id=1] Check soft queue:null Weak Reference Object run get():null Check weak queue:java.lang.ref.WeakReference@61de33 Phantom Reference Object run get():null Check Phantom queue:null Check Phantom queue:java.lang.ref.PhantomReference@ca0b6 Count for 2 times

    可以看到 , 當(dāng)運行了系統(tǒng)回收后 , 虛引用與弱引用被回收 , 由于內(nèi)存并不吃緊 , 軟引用依然保持原樣 . 弱引用立即被加入了隊列 , 而虛引用則在循環(huán)兩次的手動調(diào)用 GC 后被加入了隊列 . 其次 , 采用的環(huán)境是 IBM JDK 6, 結(jié)果如下 :

    清單 16. IBM JDK 6 下測試結(jié)果
    The Reference is java.lang.ref.SoftReference@3a2c3a2c and with object RefTestObj@391e391e[id=1] which is not null The Reference is java.lang.ref.WeakReference@3a303a30 and with object RefTestObj@39203920[id=2] which is not null The Reference is java.lang.ref.PhantomReference@3a353a35 and with object null which is null Soft Reference Object run get():RefTestObj@391e391e[id=1] Check soft queue:null Weak Reference Object run get():RefTestObj@39203920[id=2] Check weak queue:null Phantom Reference Object run get():null Check Phantom queue:null Object [958544162][id=3] come into finalize Object [958413088][id=2] come into finalize Soft Reference Object run get():RefTestObj@391e391e[id=1] Check soft queue:null Weak Reference Object run get():null Check weak queue:java.lang.ref.WeakReference@3a303a30 Phantom Reference Object run get():null Check Phantom queue:null Object [958282014][id=1] come into finalize ............

    程序運行到這里進入了無限循環(huán),必須手動終止。比對上下兩份結(jié)果可以看到,當(dāng)多次運行系統(tǒng)垃圾回收后,IBM JVM 將軟引用一并加入了回收隊列中,并運行了其 finalize 方法。另外,即使經(jīng)過很多次系統(tǒng)垃圾回收,虛引用也沒有被加入到隊列中去。不知道這是不是 IBM JVM 的一個小小的 BUG 所在。

    結(jié)論

    • SoftReference 中 Oracle JVM 的表現(xiàn)滿足規(guī)范,只當(dāng)內(nèi)存不足時才進行回收。而 IBM JVM 的策略則更為積極,在內(nèi)存尚且充足的情況下也進行了回收,值得注意。
    • PhantomReference 中 Oracle JVM 的表現(xiàn)滿足規(guī)范,執(zhí)行 finalize 后若干次 GC 就被添加到了 Queue 中。而 IBM JVM 則始終沒有被添加到 Queue 中導(dǎo)致了死循環(huán)。所以在使用 PhantomReference 時出現(xiàn)類似的情況時,可以考慮是否是因為使用了不同 JVM 所導(dǎo)致。

    小結(jié)

    本文深入地介紹了 java.lang.ref 包使用方法,并結(jié)合實驗分析了包內(nèi)不同類的表現(xiàn)。同時對該包在不同 Java 虛擬機上的表現(xiàn)進行了深入地分析。

    原文:http://www.ibm.com/developerworks/cn/java/j-lo-langref/

    轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4049434.html

    總結(jié)

    以上是生活随笔為你收集整理的深入探讨 java.lang.ref 包--转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。