Java 中的四種引用類型


  

一、背景

Java的內存回收不需要程序員負責,JVM會在必要時啟動Java GC完成垃圾回收。

Java以便我們控制對象的生存周期,提供給了我們四種引用方式,引用強度從強到弱分別為:強引用、軟引用、弱引用、虛引用。

1. 強引用 StrongReference
2. 弱引用 WeakReference
3. 軟引用 SoftReference
4. 虛引用 PhantomReference

 

二、簡介

1. 強引用 StrongReference

StrongReference是Java的默認引用形式,使用時不需要顯示定義。任何通過強引用所使用的對象不管系統資源有多緊張,Java GC都不會主動回收具有強引用的對象。

public class StrongReferenceTest {

    public static int M = 1024*1024;

    public static void printlnMemory(String tag){
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n"+tag+":");
        System.out.println(runtime.freeMemory()/M+"M(free)/" + runtime.totalMemory()/M+"M(total)");
    }
    
    public static void main(String[] args){
        StrongReferenceTest.printlnMemory("1.原可用內存和總內存");
        
        //實例化10M的數組並與strongReference建立強引用
        byte[] strongReference = new byte[10*StrongReferenceTest.M];
        StrongReferenceTest.printlnMemory("2.實例化10M的數組,並建立強引用");
        System.out.println("strongReference : "+strongReference);
        
        System.gc();
        StrongReferenceTest.printlnMemory("3.GC后");
        System.out.println("strongReference : "+strongReference);

        //strongReference = null;后,強引用斷開了
        strongReference = null;
        StrongReferenceTest.printlnMemory("4.強引用斷開后");
        System.out.println("strongReference : "+strongReference);
        
        System.gc();
        StrongReferenceTest.printlnMemory("5.GC后");
        System.out.println("strongReference : "+strongReference);
        }
}

 

 

 

2. 弱引用 WeakReference

如果一個對象只具有弱引用,無論內存充足與否,Java GC后對象如果只有弱引用將會被自動回收。

package com.mzj.reference;

import java.lang.ref.WeakReference;

/**
 * 2.弱引用 WeakReference
 * 如果一個對象只具有弱引用,無論內存充足與否,Java GC后對象如果只有弱引用將會被自動回收。
 */
public class WeakReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = WeakReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }

    public static void main(String[] args) {
        WeakReferenceTest.printlnMemory("1.原可用內存和總內存");

        //創建弱引用
        WeakReference<Object> weakRerference = new WeakReference<>(new byte[10 * WeakReferenceTest.M]);
        WeakReferenceTest.printlnMemory("2.實例化10M的數組,並建立弱引用");
        System.out.println("weakRerference.get() : " + weakRerference.get());

        System.gc();
        StrongReferenceTest.printlnMemory("3.GC后");
        System.out.println("weakRerference.get() : " + weakRerference.get());
    }
}

 

 

 

 

3. 軟引用 SoftReference

軟引用和弱引用的特性基本一致, 主要的區別在於軟引用在內存不足時才會被回收。如果一個對象只具有軟引用,Java GC在內存充足的時候不會回收它,內存不足時才會被回收。

package com.mzj.reference;

import java.lang.ref.SoftReference;

/**
 * 3.軟引用 SoftReference
 * 軟引用和弱引用的特性基本一致, 主要的區別在於軟引用在內存不足時才會被回收。
 * 如果一個對象只具有軟引用,Java GC在內存充足的時候不會回收它,內存不足時才會被回收。
 */
public class SoftReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = StrongReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }

    public static void main(String[] args) {
        SoftReferenceTest.printlnMemory("1.原可用內存和總內存");

        //建立軟引用
        SoftReference<Object> softRerference = new SoftReference<Object>(new byte[10 * SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("2.實例化10M的數組,並建立軟引用");
        System.out.println("softRerference.get() : " + softRerference.get());

        System.gc();
        SoftReferenceTest.printlnMemory("3.內存可用容量充足,GC后");
        System.out.println("softRerference.get() : " + softRerference.get());

        //實例化一個4M的數組,使內存不夠用,並建立軟引用
        //free=10M=4M+10M-4M,證明內存可用量不足時,GC后byte[10*m]被回收
        SoftReference<Object> softRerference2 = new SoftReference<Object>(new byte[4 * SoftReferenceTest.M]);
        SoftReferenceTest.printlnMemory("4.實例化一個4M的數組后");
        System.out.println("softRerference.get() : " + softRerference.get());
        System.out.println("softRerference2.get() : " + softRerference2.get());
    }
}

 

 

4. 虛引用 PhantomReference

從PhantomReference類的源代碼可以知道,它的get()方法無論何時返回的都只會是null。

所以單獨使用虛引用時,沒有什么意義,需要和引用隊列ReferenceQueue類聯合使用。

當執行Java GC時如果一個對象只有虛引用,就會把這個對象加入到與之關聯的ReferenceQueue中。

package com.mzj.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

/**
 * 4.虛引用 PhantomReference
 * 從PhantomReference類的源代碼可以知道,它的get()方法無論何時返回的都只會是null。
 * 所以單獨使用虛引用時,沒有什么意義,需要和引用隊列ReferenceQueue類聯合使用。
 * 當執行Java GC時如果一個對象只有虛引用,就會把這個對象加入到與之關聯的ReferenceQueue中。
 */
public class PhantomReferenceTest {

    public static int M = 1024 * 1024;

    public static void printlnMemory(String tag) {
        Runtime runtime = Runtime.getRuntime();
        int M = PhantomReferenceTest.M;
        System.out.println("\n" + tag + ":");
        System.out.println(runtime.freeMemory() / M + "M(free)/" + runtime.totalMemory() / M + "M(total)");
    }

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

        PhantomReferenceTest.printlnMemory("1.原可用內存和總內存");
        byte[] object = new byte[10 * PhantomReferenceTest.M];
        PhantomReferenceTest.printlnMemory("2.實例化10M的數組后");

        //建立虛引用
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(object, referenceQueue);

        PhantomReferenceTest.printlnMemory("3.建立虛引用后");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());

        //斷開byte[10*PhantomReferenceTest.M]的強引用
        object = null;
        PhantomReferenceTest.printlnMemory("4.執行object = null;強引用斷開后");

        System.gc();
        PhantomReferenceTest.printlnMemory("5.GC后");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("phantomReference.get() : " + phantomReference.get());
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());

        //斷開虛引用
        phantomReference = null;
        System.gc();
        PhantomReferenceTest.printlnMemory("6.斷開虛引用后GC");
        System.out.println("phantomReference : " + phantomReference);
        System.out.println("referenceQueue.poll() : " + referenceQueue.poll());
    }
}

 

 

三、小結

1、強引用是 Java 的默認引用形式,使用時不需要顯示定義,是我們平時最常使用到的引用方式。

   不管系統資源有多緊張,Java GC都不會主動回收具有強引用的對象。

2、弱引用和軟引用一般在引用對象為非必需對象的時候使用。

    它們的區別是被弱引用關聯的對象在垃圾回收時總是會被回收,被軟引用關聯的對象只有在內存不足時才會被回收。

    虛引用的get()方法獲取的永遠是null,無法獲取對象實例。Java GC會把虛引用的對象放到引用隊列里面。

    可用來在對象被回收時做額外的一些資源清理或事物回滾等處理。

3、由於無法從虛引獲取到引用對象的實例。它的使用情況比較特別,所以這里不把虛引用放入表格進行對比。

這里對強引用、弱引用、軟引用進行對比:

引用類型 GC時JVM內存充足 GC時JVM內存不足
強引用 不被回收 不被回收
弱引用 被回收 被回收
軟引用 不被回收 被回收
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM