【Android 內存優化】Java 引用類型 ( 強引用 | 軟引用 | 弱引用 | 虛引用 )



強引用 ( 不回收 ) > 軟引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虛引用 ( 回收前通知 )

如果單純的想避免 OOM , 可以使用軟引用 ;

如果對性能要求很高 , 要最大限度節省內存 , 所有的內存回收都要及時處理 , 就使用弱引用 ;

內存泄漏原理 : 長生命週期對象 , 持有短生命週期對象的引用 , 並且是強引用持有 , GC 無法釋放該短生命週期對象引用 , 造成 OOM ;





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



1. 引用 :

① 變量引用 : 創建對象 , 將對象賦值給變量 VV , 那麼變量 VV 就是對象的引用 ;

② 對象引用 : 對象 AA 可以調用對象 BB , 那麼對象 AA 持有對象 BB 的引用 ;



2. 引用類型 : GC 垃圾回收機制 引用類型 密切相關 ;


① 強引用 : 最普遍的引用 , Object object = new Object() , 這就是強引用 ;

② 軟引用 : 用於定義一些有用 , 但不是必須的對象 , 對象被軟引用引用 , 當系統內存嚴重不足 , 在報出 OutOfMemoryError 錯誤之前就會將軟件用釋放掉 , 如果將軟引用釋放掉之後 , 還是內存不足 , 就會報 OutOfMemoryError 異常 ;

③ 弱引用 : 弱引用也是描述非必須對象 , 該引用的引用強度比軟引用更弱 , 該引用對象 , 生命週期只到下一次 GC 回收之前 , GC 只要掃描到了弱引用 , 直接回收 , 即使內存足夠 , 也要回收該對象所佔用的內存 ;

④ 虛引用 : 虛引用無法獲得被引用的對象 , 其唯一的作用是對象被回收時 , 可以得到相應的通知 ; 虛引用不會對對象的生存週期造成影響任何 , GC 忽略虛引用 , 即使有虛引用引用某對象 , GC 會當做該引用不存在 ; 開發時不常用 , 一般開發底層 SDK , 或監測系統運行類的軟件時 , 才使用 ;


強引用 ( 不回收 ) > 軟引用 ( OOM 前回收 ) > 弱引用 ( GC 必回收 ) > 虛引用 ( 回收前通知 )





二、 軟引用代碼示例



1. 軟引用創建 :


① 直接創建軟引用 : 在軟件用構造函數中創建一個軟引用 , 不涉及外部引用 ;

SoftReference<Object> softReference3 = new SoftReference<>(new Object());

② 傳入創建好的對象引用創建軟引用 : 軟引用是通過在 軟引用構造函數 傳入引用對象創建的 , 首先要創建引用對象 ; 下面的強引用可以在創建軟引用完畢後置空解除引用 , 否則肯定不能被回收 ;

Object softObject = new Object();
SoftReference<Object> softReference1 = new SoftReference<>(softObject);

③ 創建軟引用並設置引用隊列 : 在上述基礎上 , 可以在構造函數中設置一個引用隊列參數 ;

Object softObject = new Object();
// 引用隊列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 創建軟引用, 並將該軟引用引用放入引用隊列中
SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);

④ 上述軟引用釋放 : 創建的軟引用在兩種情況下才會釋放 :

  • 軟引用置空 : GC Root 不可達時回收 ;
  • 內存不足 : 內存不足 , OOM 之前回收 ;


2. 軟引用代碼示例 :

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

public class Main {

    public static void main(String[] args) throws InterruptedException {

        /*
            軟引用示例
            1. 創建軟引用 : public SoftReference(T referent)
            2. 創建軟引用並放入引用隊列中 :
                public SoftReference(T referent, ReferenceQueue<? super T> q)

            回收時機 : 軟引用在內存不足時纔會被回收, OOM 的前一刻
         */
        Object softObject = new Object();

        // 1. 直接創建軟引用
        SoftReference<Object> softReference1 = new SoftReference<>(softObject);

        // 2. 創建軟引用並放入引用隊列中

        // 引用隊列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 創建軟引用, 並將該軟引用引用放入引用隊列中
        SoftReference<Object> softReference2 = new SoftReference<>(softObject, referenceQueue);


        // 3. 直接創建軟件用
        SoftReference<Object> softReference3 = new SoftReference<>(new Object());


        //獲取軟引用, 調用軟引用的 get() 方法 , 即可獲取軟引用
        softReference1.get();
        softReference2.get();

        // 打印軟件用
        System.out.println("softReference1 : " + softReference1.get());
        System.out.println("softReference2 : " + softReference2.get());
        System.out.println("softReference3 : " + softReference3.get());

        // 軟件用被回收之後, 可以通過調用 referenceQueue.poll() 方法, 獲取到被回收的引用
        // 如果獲取到 null, 說明該軟引用沒有被回收
        // 如果該軟件用被回收, 可以獲取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());


        // 驗證回收機制, 將軟引用對象設置爲空
        // 其對應的軟引用對象 SoftReference<Object> 回收的時機
        //  - 1. 置空訪問不可達時回收
        //  - 2. 內存不足 OOM 之前回收
        softObject = null;
        // 申請進行垃圾回收, 這裏只是申請垃圾回收, 並不會馬上調用, 大約幾秒內會進行垃圾回收
        System.gc();

        Thread.sleep(5_000);

        // 打印相關變量驗證回收機制
        System.out.println("");
        System.out.println("softReference1 : " + softReference1.get());
        System.out.println("softReference2 : " + softReference2.get());
        System.out.println("softReference3 : " + softReference3.get());

        // 軟件用被回收之後, 可以通過調用 referenceQueue.poll() 方法, 獲取到被回收的引用
        // 如果獲取到 null, 說明該軟引用沒有被回收
        // 如果該軟件用被回收, 可以獲取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
    }

}


執行結果 :

softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null

softReference1 : java.lang.Object@1b6d3586
softReference2 : java.lang.Object@1b6d3586
softReference3 : java.lang.Object@4554617c
referenceQueue.poll : null




三、 弱引用代碼示例



1. 弱引用創建 :


① 直接創建弱引用 : 在軟件用構造函數中創建一個軟引用 , 不涉及外部引用 ;

WeakReference<Object> weakReference3 = new WeakReference<>(new Object());

② 傳入創建好的對象引用創建弱引用 : 弱引用是通過在 弱引用構造函數 傳入引用對象創建的 , 首先要創建引用對象 ; 下面的強引用可以在創建軟引用完畢後置空解除引用 , 否則肯定不能被回收 ;

Object softObject = new Object();
WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);

③ 創建弱引用並設置引用隊列 : 在上述基礎上 , 可以在構造函數中設置一個引用隊列參數 ;

Object softObject = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 創建弱引用, 並將該弱引用引用放入引用隊列中
WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);

④ 上述軟引用釋放 : 弱引用不管是否置空 , 只要遇到 GC , 一律回收



2. 軟引用代碼示例 :

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

public class Main {

    public static void main(String[] args) throws InterruptedException {

        /*
            弱引用示例
            1. 創建弱引用 : public WeakReference(T referent)
            2. 創建弱引用並放入引用隊列中 :
                public WeakReference(T referent, ReferenceQueue<? super T> q)

            回收時機 : 弱引用在 GC 時被回收, 不管內存是否足夠
         */
        Object weakObject = new Object();

        // 1. 直接創建弱引用
        WeakReference<Object> WeakReference1 = new WeakReference<>(weakObject);

        // 2. 創建弱引用並放入引用隊列中

        // 引用隊列
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 創建弱引用, 並將該弱引用引用放入引用隊列中
        WeakReference<Object> weakReference2 = new WeakReference<>(weakObject, referenceQueue);


        // 3. 直接創建弱引用
        WeakReference<Object> weakReference3 = new WeakReference<>(new Object());


        //獲取弱引用, 調用弱引用的 get() 方法 , 即可獲取弱引用
        WeakReference1.get();
        weakReference2.get();

        // 打印軟件用
        System.out.println("WeakReference1 : " + WeakReference1.get());
        System.out.println("WeakReference2 : " + weakReference2.get());
        System.out.println("WeakReference3 : " + weakReference3.get());

        // 弱引用被回收之後, 可以通過調用 referenceQueue.poll() 方法, 獲取到被回收的引用
        // 如果獲取到 null, 說明該弱引用沒有被回收
        // 如果該軟件用被回收, 可以獲取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());


        // 驗證回收機制, 將弱引用對象設置爲空
        // 這裏弱引用不管是否置空 , 只要遇到 GC , 一律回收
        weakObject = null;
        // 申請進行垃圾回收, 這裏只是申請垃圾回收, 並不會馬上調用, 大約幾秒內會進行垃圾回收
        System.gc();

        Thread.sleep(5_000);

        // 打印相關變量驗證回收機制
        System.out.println("");
        System.out.println("WeakReference1 : " + WeakReference1.get());
        System.out.println("WeakReference2 : " + weakReference2.get());
        System.out.println("WeakReference3 : " + weakReference3.get());

        // 軟件用被回收之後, 可以通過調用 referenceQueue.poll() 方法, 獲取到被回收的引用
        // 如果獲取到 null, 說明該弱引用沒有被回收
        // 如果該軟件用被回收, 可以獲取非空值
        System.out.println("referenceQueue.poll : " + referenceQueue.poll());
    }

}


執行結果 :

WeakReference1 : java.lang.Object@1b6d3586
WeakReference2 : java.lang.Object@1b6d3586
WeakReference3 : java.lang.Object@4554617c
referenceQueue.poll : null

WeakReference1 : null
WeakReference2 : null
WeakReference3 : null
referenceQueue.poll : java.lang.ref.WeakReference@74a14482
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章