天天看點

JVM垃圾回收-強引用, 軟引用, 弱引用, 虛引用以及終結器引用(三)概述

String Reference, Soft Reference, Weak Reference, Phantom Reference, Final Reference

  • 概述
    • 強引用(String Reference)
    • 軟引用(Soft Reference)
    • 弱引用(Weak Reference)
    • 虛引用(Phantom Reference)
    • 終結器引用(Final Reference)

概述

  • 對象的引用級别為4種, 由高到低依次分别為: 強引用, 軟引用, 弱引用和虛引用

強引用(String Reference)

  • 使用 new操作符建立的對象, 并将其指派給一個變量的時候, 此變量就成了指向該對象的一個強引用. 也就是預設的引用方式
  • 當記憶體空間不足時, JVM甯願抛出 OOM異常, 也不會靠釋放具有強引用的對象來解決記憶體不足的問題
  • 執行個體:
Object obj = new Object();

           

軟引用(Soft Reference)

  • 軟引用對象是在 JVM記憶體不足時才會被回收(如果被強引用關聯着就不會被回收)
  • 軟引用通常實作緩存相關的 如高速緩存(當有空閑記憶體, 就會保留緩存, 否則清理掉)就是用到軟引用, 還有 mybatis内部類也有使用軟引用
  • 執行個體:
/**
 * 設定堆空間大小: -Xms3m -Xmx3m -XX:+PrintGCDetails
 * */
public class SoftReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 建立 `objA`強引用變量*/
        byte[] objA = new byte[1024 * 1024];
        /** 建立軟引用變量*/
        SoftReference<byte[]> reference = new SoftReference<>(objA);
        if (reference.get() == null) {
            System.out.println("reference is null");
        } else {
            System.out.println("reference is " + reference.get());
        }
        /** 将強引用删除(為了與`軟引用對象 reference`的聯系給失效), 然後調用垃圾回收*/
        objA = null;
        //System.gc();
        /** 由于 Finalizer線程優先級較低, 暫停2秒, 為了保證 GC的執行*/
        //Thread.sleep(2000);
        /** 建立 `objB`強引用變量
         * - 建立以下資料的同時, 由于老年代空間不足, 會觸發 Full GC, 但是容量依然不夠, 是以會将軟引用删除, 以此維持此次的執行程序
         * */
        byte[] objB = new byte[1024 * 2024];

        System.out.println("reference is " + reference.get());
    }
}

輸出:
[GC (Allocation Failure) [PSYoungGen: 512K->384K(1024K)] 512K->384K(3584K), 0.0012481 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 892K->464K(1024K)] 892K->464K(3584K), 0.0016060 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 976K->496K(1024K)] 976K->504K(3584K), 0.0011251 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
reference is [[email protected]
[GC (Allocation Failure) [PSYoungGen: 675K->496K(1024K)] 1707K->1544K(3584K), 0.0009048 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 496K->448K(1024K)] 1544K->1496K(3584K), 0.0007774 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 448K->0K(1024K)] [ParOldGen: 1048K->1438K(2560K)] 1496K->1438K(3584K), [Metaspace: 3285K->3285K(1056768K)], 0.0070078 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] 1438K->1438K(3584K), 0.0004453 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] [ParOldGen: 1438K->397K(2560K)] 1438K->397K(3584K), [Metaspace: 3285K->3285K(1056768K)], 0.0060445 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
reference is null # 軟引用被釋放是以輸出 null

           
  • 當執行 Major GC/Full GC後發現記憶體還是不足時, 會嘗試第二次回收, 這一次會将軟引用對象回收掉. 最後如果依然不足就會抛異常

弱引用(Weak Reference)

  • 弱引用對象隻能生存到下一次垃圾回收之前, 也就是無論記憶體空間是否足夠都會回收(如果被強引用關聯着就不會被回收)
  • 弱引用也用與緩存
  • 執行個體:
/**
 * 設定堆空間大小: -Xms3m -Xmx3m -XX:+PrintGCDetails
 * */
public class WeakReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 建立 `objA`強引用變量*/
        byte[] objA = new byte[1024 * 1024];
        /** 建立弱引用變量*/
        WeakReference<byte[]> reference = new WeakReference<>(objA);
        if (reference.get() == null) {
            System.out.println("reference is null");
        } else {
            System.out.println("reference is " + reference.get());
        }
        /** 将強引用删除(為了與`弱引用對象 reference`的聯系給失效), 然後調用垃圾回收*/
        objA = null;
        System.gc();
        /** 由于 Finalizer線程優先級較低, 暫停2秒, 為了保證 GC的執行*/
        Thread.sleep(2000);
        /** 建立 `objB`強引用變量
         * - 建立以下資料的同時, 由于老年代空間不足, 會觸發 Full GC, 但是容量依然不夠, 是以會将弱引用删除, 以此維持此次的執行程序
         * */
        byte[] objB = new byte[1024 * 1024];

        System.out.println("reference is " + reference.get());
    }
}

輸出:
[GC (Allocation Failure) [PSYoungGen: 512K->384K(1024K)] 512K->384K(3584K), 0.0013732 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 892K->464K(1024K)] 892K->464K(3584K), 0.0014486 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 974K->464K(1024K)] 974K->472K(3584K), 0.0011306 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
reference is [[email protected]
[GC (System.gc()) [PSYoungGen: 584K->512K(1024K)] 1616K->1552K(3584K), 0.0010554 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 512K->0K(1024K)] [ParOldGen: 1040K->388K(2560K)] 1552K->388K(3584K), [Metaspace: 3242K->3242K(1056768K)], 0.0061729 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 504K->368K(1024K)] 892K->764K(3584K), 0.0110091 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
reference is null # 弱引用被釋放是以輸出 null

           

虛引用(Phantom Reference)

  • 一個對象是否有虛引用, 完全不會對其生命周期構成影響(最弱的引用), 也無法通過虛引用來獲得一個對象的執行個體
  • 虛引用必須與引用隊列一起使用. 如果垃圾收集器回收時, 發現有虛引用, 就會回收對象後, 将虛引用對象加入到引用隊列中
  • 對象設定虛引用的唯一目的就是能在這個對象被回收時收到一個事件(就是對象回收跟蹤的)
  • 執行個體:
public class PhantomReferenceApp {
    public static void main(String[] args) throws Exception {
        /** 建立引用隊列, 為綁定虛引用*/
        ReferenceQueue referenceQueue = new ReferenceQueue();
        /** 建立 `app`強引用*/
        PhantomReferenceApp app = new PhantomReferenceApp();
        /** 建立虛引用*/
        PhantomReference data = new PhantomReference(app, referenceQueue);
        System.out.println("before gc: " + data.get() + ", " + referenceQueue.poll());
        /** 将強引用删除, 然後調用垃圾回收*/
        app = null;
        System.gc();
        /** 由于 Finalizer線程優先級較低, 暫停2秒, 為了保證 GC的執行*/
        Thread.sleep(2000);
        System.out.println("after gc: " + data.get() + ", " + referenceQueue.poll());
    }
}

輸出:
> before gc: null, null # 擷取不到任何東西 null
> after gc: null, [email protected] # 但是引用隊列收到了相關事件

           

終結器引用(Final Reference)

  • 終結器引用的實作就是 Jdk的 finalization機制, 将重寫了 finalize()方法的對象加入到 F-Queue隊列(引用隊列)中, 并賦予3種狀态: 可觸及的, 可複活的和不可觸及的. 使用詳情參考: https://blog.csdn.net/qcl108/article/details/108835348
如果您覺得有幫助,歡迎點贊哦 ~ 謝謝!!