Java 四種引用類型(強引用、軟引用、弱引用、虛引用)

概述

Java 中的引用類似 C 語言中的指針,指向一個對象,比如:

// person 就是指向 Person 實例“張三”的引用
Person person = new Person("張三");

在 JDK1.2 以前,Java 裏的引用是很傳統的定義:如果 reference 類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱該 reference 數據是代表某塊內存、某個對象的引用

這種定義當然沒有什麼不對,但現在看來顯得太狹隘了,比如我們希望描述一類對象:當內存空間足夠時,能保留在內存中,如果內存空間在進行了垃圾收集後仍然緊張,則可以拋棄這些對象,很多系統的緩存功能都符合這樣的應用場景

JDK1.2 對引用的概念作了補充,將引用分爲強引用(Strongly Reference)、軟引用(SoftReference)、弱引用(Weak Reference)和虛引用(Phantom Reference),強度依次減弱


強引用

Java 中最常見的就是強引用,把一個對象賦給一個引用變量,這個引用變量就是一個強引用。類似 Object obj = new Object()

當一個對象被強引用變量引用時,除非超過了引用的作用域或者顯示地將相應強引用賦值爲 null,否則是不可能被垃圾回收器回收的


軟引用

軟引用用來描述一些有用但非必須的對象,此類對象只有在進行一次垃圾收集仍然沒有足夠內存時,纔會在第二次垃圾收集時被回收,需要用 java.lang.ref.SoftReference 類來實現

public class SoftRefenenceDemo {

    public static void main(String[] args) {
        softRefMemoryEnough();
        System.out.println("------------");
        softRefMemoryNotEnough();
    }

    private static void softRefMemoryEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);  // java.lang.Object@2503dbd3
        System.out.println(s1.get());  // java.lang.Object@2503dbd3

        o1 = null;
        System.gc();

        System.out.println(o1);  // null
        System.out.println(s1.get());  // java.lang.Object@2503dbd3
    }

     /**
      * JVM配置 -Xms5m -Xmx5m ,故意 new 一個大對象,使內存不足產生 OOM,看軟引用回收情況
      */
    private static void softRefMemoryNotEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);  // java.lang.Object@4b67cf4d
        System.out.println(s1.get());  // java.lang.Object@4b67cf4d

        o1 = null;
        
        try {
          byte[] bytes = new byte[10 * 1024 * 1024];
        } catch(Error e) {
          e.printStackTrace();
        }
        

        System.out.println(o1);  // null
        System.out.println(s1.get());  // null
    }
}

弱引用

弱引用用來描述那些非必須對象,但它的強度比軟引用更弱一些。被軟引用關聯的對象只能生存到下一次垃圾收集發生爲止,當垃圾收集器開始工作,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象,需要用 java.lang.ref.WeakReference 類來實現

public class WeakReferenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> w1 = new WeakReference<Object>(o1);

        System.out.println(o1);  // java.lang.Object@7440e464
        System.out.println(w1.get());  // java.lang.ref.WeakReference@49476842

        o1 = null;
        System.gc();

        System.out.println(o1);  // null
        System.out.println(w1.get());  // null
    }
}

虛引用

虛引用,顧名思義,就是形同虛設,與其他幾種引用都不太一樣,一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用必須和引用隊列(RefenenceQueue)聯合使用。虛引用的主要作用是跟蹤對象垃圾回收的狀態,僅僅是提供了一種確保對象被 finalize 以後,收到一個系統通知或者後續添加進一步的處理

public class PhantomReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(o1,referenceQueue);

        System.out.println(o1);  // java.lang.Object@7440e464
        System.out.println(referenceQueue.poll());  // null
        System.out.println(phantomReference.get());  // null

        o1 = null;
        System.gc();
        Thread.sleep(3000);

        System.out.println(o1);  // null
        System.out.println(referenceQueue.poll()); // java.lang.ref.PhantomReference@49476842
        System.out.println(phantomReference.get());  // null
    }

}

ReferenceQueue 是用來配合引用工作的,沒有ReferenceQueue 一樣可以運行。SoftReference、WeakReference、PhantomReference 都有一個可以傳遞 ReferenceQueue 的構造器。創建引用的時候,可以指定關聯的隊列,當 GC 釋放對象內存的時候,會將引用加入到引用隊列。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動,這相當於是一種通知機制。當關聯的引用隊列中有數據的時候,意味着指向的堆內存中的對象被回收。通過這種方式,JVM 允許我們在對象被銷燬後,做一些我們自己想做的事情

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章