1. 程式人生 > >Java 中的四種引用

Java 中的四種引用

之前我們提到過 GC,但當 Java 中引用的物件越來越多,會導致記憶體空間不足,最終會產生錯誤 OutOfMemoryError,並讓應用程式終止。那為什麼 GC 在此時不能多收集一些物件呢?這就和今天說的引用型別有關了。

首先,從 JDK1.2 開始,物件的引用被劃分為4種級別,從而使程式能更加靈活地控制物件的生命週期。這4種級別由高到低依次為:強引用軟引用弱引用虛引用

強引用

強引用(Strong Reference)是使用最普遍的引用。如果一個物件具有強引用,那麼它永遠不會被 GC。例如:

    Object strongReference = new Object();

當記憶體空間不足時,JVM 寧願丟擲OutOfMemoryError

,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。

如果強引用物件不使用時,需要弱化從而可以被 GC,例如ArrayList中的clear()方法:

    /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

顯式地設定強引用物件為null,或讓其超出物件的生命週期範圍,則垃圾回收器認為該物件不存在引用,就會回收這個物件。具體什麼時候收集這要取決於具體的垃圾回收器。

軟引用

如果一個物件只具有軟引用(Soft Reference),當記憶體空間充足時,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。讓我們來看一個例子具體瞭解一下:

    String str = new String("abc");
    SoftReference<String> softReference = new SoftReference<>(str);
    String result = softReference.get();

讓我們來看一下get()

    public T get() {
        T o = super.get();
        // timestamp代表上一次軟引用上一次被使用的時間(初始化、get())
        // clock代表上一次GC的時間
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }

因此,軟引用在被垃圾回收時,也遵循LRU法則,優先回收最近最少被使用的物件進行回收。

軟引用的使用場景多是記憶體敏感的快取記憶體。具體來說,就是我們希望將資料存放到快取中,這樣可以快速進行讀取。但是,當 JVM 中記憶體不夠用時,我們又不希望快取資料會佔用到 JVM 的記憶體。例如配合ReferenceQueue,如果軟引用所引用物件被垃圾回收,JVM 就會把這個軟引用加入到與之關聯的引用佇列中:

    ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
    String str = new String("abc");
    SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);

    str = null;
    // Notify GC
    System.gc();

    System.out.println(softReference.get()); // abc

    Reference<? extends String> reference = referenceQueue.poll();
    System.out.println(reference); //null

但是需要注意的是,如果使用軟引用快取,有可能導致Full GC增多。

弱引用

如果一個物件只具有弱引用(Weak Reference),其生命週期相比於軟引用更加短暫。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會對它進行回收。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。其使用為:

    String str = new String("abc");
    WeakReference<String> weakReference = new WeakReference<>(str);
    str = weakReference.get();

講到弱引用,就不得不提到WeakHashMap。和HashMap相比,當我們給 JVM 分配的記憶體不足的時候,HashMap 寧可丟擲 OutOfMemoryError 異常,也不會回收其相應的沒有被引用的物件,而 WeakHashMap 則會回收儲存在其中但有被引用的物件。

WeakHashMap 通過將一些沒有被引用的鍵的值賦值為 null ,這樣的話就會告知GC去回收這些儲存的值了。假如我們特地傳入 key 為 null 的鍵,WeakHashMap 會將鍵設定為特殊的 Oject,原始碼為:

    public V put(K key, V value) {
        // key會被重新賦值
        Object k = maskNull(key);
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int i = indexFor(h, tab.length);

        for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
            if (h == e.hash && eq(k, e.get())) {
                V oldValue = e.value;
                if (value != oldValue)
                    e.value = value;
                return oldValue;
            }
        }

        modCount++;
        Entry<K,V> e = tab[i];
        tab[i] = new Entry<>(k, value, queue, h, e);
        if (++size >= threshold)
            resize(tab.length * 2);
        return null;
    }

    /**
     * Value representing null keys inside tables.
     * 特殊的key
     */
    private static final Object NULL_KEY = new Object();

    /**
     * Use NULL_KEY for key if it is null.
     */
    private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }

虛引用

虛引用(PhantomReference),顧名思義,就是形同虛設。與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

虛引用主要用來跟蹤物件被垃圾回收器回收的活動。 虛引用與軟引用和弱引用的一個區別在於:

虛引用必須和引用佇列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之關聯的引用佇列中。

例如:

    String str = new String("abc");
    ReferenceQueue queue = new ReferenceQueue();
    // 建立虛引用,要求必須與一個引用佇列關聯
    PhantomReference pr = new PhantomReference(str, queue);

程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要進行垃圾回收。如果程式發現某個虛引用已經被加入到引用佇列,那麼就可以在所引用的物件的記憶體被回收之前採取必要的行動,也可以理解為一種回撥方法。

總結

Java 中4種引用的級別和強度由高到低依次為:強引用 -> 軟引用 -> 弱引用 -> 虛引用

通過表格,說明其特性:

引用型別 被垃圾回收的時間 使用場景 生存時間
強引用 從來不會 物件的一般狀態 JVM停止執行時
軟引用 記憶體不足時 物件快取 記憶體不足時
弱引用 正常垃圾回收時 物件快取 垃圾回收後終止
虛引用 正常垃圾回收時 跟蹤物件的垃圾回收 垃圾回收後終止

有興趣的話可以訪問我的部落格或者關注我的公眾號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

相關推薦

Java引用的區分

isn 賦值 public medium 是否 comm 回調 ant container 強引用(StrongReference) 強引用就是指在程序代碼之中普遍存在的,比如下面這段代碼中的object和str都是強引用: 1 2 Object obje

基礎 | Java引用的區別

在JDK 1.2以前,Java中的引用定義為:如果引用型別的資料中儲存的數值代表的是另一塊記憶體的起始地址,則這塊記憶體即代表著一個引用。故在該定義下,一個Java物件僅有被應用和未被引用兩種狀態。 在JDK 1.2之後,Java對引用的概念進行了擴充,將引用分為強引用、軟引用

java引用型別

 今天看程式碼,裡面有一個類java.lang.ref.SoftReference把小弟弄神了,試想一下,接觸java已經有3年了哇,連lang包下面的類都不瞭解,怎麼混。後來在網上查資料,感覺收穫頗多,現記錄如下。        物件的強、軟、弱和虛引用在JDK 1.2以

Java引用強 軟 弱 虛 的詳細介紹及理解

 Java中強軟弱虛四種引用 一:引用的目的: 在Java中垃圾回收器的執行是JVM操作的,但是我們仍然可以在一定程度上與垃圾回收器進行互動,其目的在於更好的幫助垃圾回收器管理好應用的記憶體,避免記憶

Java引用(強、軟、弱、虛)

1. 強引用(StrongReference)  強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。 2. 軟引用(SoftR

Java引用 強 軟 弱 虛

前言 面試題:Java中的軟引用,弱引用在Android 是哪個的使用 目錄 一:哪四種引用 二:區別在哪 三:在Android中的使用場景 Handler 弱引用,防止記憶體洩漏

java訪問修飾符

pub oid 默認 成員變量 修飾 對象 fault 其中 () Java中的四種訪問修飾符:public、protected、default(無修飾符,默認)、private。 四種修飾符可修飾的成分(類、方法、成員變量) public protect

java訪問修飾符區別及詳解全過程

HP dnv ax1 pci gda fmm utc dos rdp 客戶端程序員:即在其應用中使用數據類型的類消費者,他的目標是收集各種用來實現快速應用開發的類。   類創建者:即創建新數據類型的程序員,目標是構建類。     訪問控制存在的原因:a、讓客戶端程序員無法觸

Java虛擬機(五)Java引用級別

tsp 進行 tro 圖片 分享 ros 通過 技術 存在 1.前言   HotSpot采取了可達性分析算法用來判斷對象是否被能被GC,無論是引用計算法還是可達性分析算法都是判斷對象是否存在引用來判斷對象是否存活。如果reference類型的數據中存儲的數值代表的是另外一塊

java修飾符(private、default、protected、public)的訪問權限

ble ted span java 修飾符 20px col family style 權限如下: no. 範圍 private default protected public 1 同一包下的同一個類 √ √ √ √ 2 同一包下的不同類 × √ √ √

你知道Java引用類型嗎

級別 block 隊列 image stringbu ror new ace 你知道 從大一自學Java已經兩年了,自覺已經可以獨當一面,(其實遠遠不足),最近一直在看書。關於java四種引用類型,我也是剛了解,特此記下! 在Java中提供了四個級別的引用:強引用,軟引

Java引用引用引用引用引用

強引用: 只要引用存在,垃圾回收器永遠不會回收 Object obj = new Object();//可直接通過obj取得對應的物件 如obj.equels(new Object()); 而這樣 obj物件對後面new Object的一個強引用,只有當obj這個引

JAVA操作xml方式的比較

. 介紹 1)DOM(JAXP Crimson解析器) DOM是用與平臺和語 言無關的方式表示XML文件的官方W3C標準。DOM是以層次結構組織的節點或資訊片斷的集合。這個層次結構允許開發人員在樹中尋找 特定資訊。分析該結構通常需要載入整個文件和構造層次結構,然後才能做任何工作。由於它是基於資訊層次

JAVA引用型別的作用——強引用、軟引用、弱引用、虛引用

java有四種引用型別,分別是強引用、軟引用、弱引用、虛引用。 背景:我們希望有這樣一種場景像快取一樣, 即:在記憶體還足夠時,希望能夠保留這些物件,當記憶體不夠時,則刪除這些物件(當然是由垃圾回收完成)。 四種引用的強度:強引用(Strong Reference)>軟引用(Sof

java引用,強弱軟虛,用到的場景

1、強引用(StrongReference)           強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。如下:                                    1 Object o=new Object();

java基礎(Java引用,強弱軟虛,用到的場景

強引用: 強引用不會被GC回收,並且在java.lang.ref裡也沒有實際的對應型別,平時工作接觸的最多的就是強引用。 Object obj = new Object();這裡的obj引用便是一個強引用。 如果一個物件具有強引用,那就類似於必不可少的生活用品,垃

Java常見的單例模式以及各自優缺點總結

餓漢式實現:餓漢式單例模式程式碼中,static變數會在類裝載時初始化,此時也不會涉及多個執行緒物件訪問該物件的問題。虛擬機器保證只會裝載一次該類,肯定不會發生併發訪問的問題。因此,可以省略synchronized關鍵字。問題:如果只是載入本類,而不是呼叫getInstan

JAVA引用,強弱軟虛以及用到的場景

1、強引用(StrongReference)           強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。如下:                                    1 Object o=new Objec

JAVA引用

1.強引用 強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。  ps:強引用其實也就是我們平時

5. Java引用,強弱軟虛,用到的場景。

1、強引用(StrongReference)           強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。如下:                                    1 Object o=new Object();   //  強引用 當記憶體空