天天看點

java強引用、軟引用、弱引用、虛引用-Java的引用類型總共有四種,你都知道嗎

目錄

​​談引用​​

​​強引用(Strong Reference)——不回收​​

​​強引用例子​​

​​軟引用(Soft Reference)——記憶體不足即回收​​

​​弱引用(Weak Reference)——發現即回收​​

​​面試題:你開發中使用過WeakHashMap嗎?​​

​​虛引用(Phantom Reference)——對象回收跟蹤​​

​​終結器引用(Final reference)​​

談引用

1.我們希望能描述這樣一類對象:當記憶體空間還足夠時,則能保留在記憶體中;如果記憶體空間在進行垃圾收集後還是很緊張,則可以抛棄這些對象。

2.【既偏門又非常高頻的面試題】強引用、軟引用、弱引用、虛引用有什麼差別?具體使用場景是什麼?

3.在JDK1.2版之後,Java對引用的概念進行了擴充,将引用分為強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)和虛引用(Phantom Reference)4種,這4種引用強度依次逐漸減弱。

4.除強引用外,其他3種引用均可以在java.lang.ref包中找到它們的身影。如下圖,顯示了這三種引用類型對應的類,開發人員可以在應用程式中直接使用它們。

java強引用、軟引用、弱引用、虛引用-Java的引用類型總共有四種,你都知道嗎

5.Reference子類中隻有終結器引用是包内可見的,其他3種引用類型均為public,可以在應用程式中直接使用。

    強引用(StrongReference):最傳統的“引用”的定義,是指在程式代碼之中普遍存在的引用指派,即類似“Object obj = new Object()”這種引用關系。無論任何情況下,隻要強引用關系還存在,垃圾收集器就永遠不會回收掉被引用的對象。

    軟引用(SoftReference):在系統将要發生記憶體溢出之前,将會把這些對象列入回收範圍之中進行第二次回收。如果這次回收還沒有足夠的記憶體,才會抛出記憶體溢出異常。

    弱引用(WeakReference):被弱引用關聯的對象隻能生存到下一次垃圾收集之前。當垃圾收集器工作時,無論記憶體空間是否足夠,都會回收掉被弱引用關聯的對象。

    虛引用(PhantomReference):一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來獲得一個對象的執行個體。為一個對象設定虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知。

強引用(Strong Reference)——不回收

1.在java程式中,最常見的引用類型是強引用(普通系統99%以上都是強引用),也就是我們最常見的普通對象引用,也是預設的引用類型。

2.當在Java語言中使用new操作符建立一個新的對象,并将其指派給一個變量的時候,這個變量就成為指向該對象的一個強引用。

3.強引用的對象是可觸及的,垃圾收集器就永遠不會回收掉被引用的對象。

4.對于一個普通的對象,如果沒有其他的引用關系,隻要超過了引用的作用域或者顯式地将相應(強)引用指派為null,就是可以當做垃圾被收集了,當然具體回收時機還是要看垃圾收集政策。

5.相對的,軟引用、弱引用和虛引用的對象是軟可觸及、弱可觸及和虛可觸及的,在一定條件下,都是可以被回收的。是以,強引用是造成Java記憶體洩漏的主要原因之一。

強引用例子

StringBuffer str = new StringBuffer("Hello world");

局部變量str指向StringBuffer執行個體所在堆空間,通過str可以操作該執行個體,那麼str就是StringBuffer執行個體的強引用。

對應記憶體結構:

java強引用、軟引用、弱引用、虛引用-Java的引用類型總共有四種,你都知道嗎

此時,如果再運作一個指派語句:

StringBuffer str1 = str;

此時對應的記憶體結構:

java強引用、軟引用、弱引用、虛引用-Java的引用類型總共有四種,你都知道嗎

強引用隻要還有引用,就不會被回收

本例中的兩個引用,都是強引用,強引用具備以下特點:

1.強引用可以直接通路目标對象。

2.強引用所指向的對象在任何時候都不會被系統回收,虛拟機甯願抛出OOM異常,也不會回收強引用所指向對象。

3.強引用可能導緻記憶體洩漏。

java強引用、軟引用、弱引用、虛引用-Java的引用類型總共有四種,你都知道嗎

軟引用(Soft Reference)——記憶體不足即回收

1.軟引用是用來描述一些還有用,但非必須的對象。隻被軟引用關聯着的對象,在系統将要發生記憶體溢出異常前,會把這些對象列進回收範圍之中進行第二次回收,如果這次回收還沒有足夠的記憶體,才會抛出記憶體溢出異常。

2.軟引用通常用來實作記憶體敏感的緩存。比如:高速緩存就有用到軟引用。如果還有空閑記憶體,就可以暫時保留緩存,當記憶體不足時清理掉,這樣就保證了使用緩存的同時,不會耗盡記憶體。

3.垃圾回收器在某個時刻決定回收軟可達的對象的時候,會清理軟引用,并可選地把引用存放到一個引用隊列(Reference Queue)。

4.類似弱引用,隻不過Java虛拟機會盡量讓軟引用的存活時間長一些,迫不得已才清理。

在JDK1.2版本之後提供了java.lang.ref.SoftReference類來實作軟引用。

Object obj = new Object(); // 聲明強引用
SoftReference<Object> sf = new SoftReference<Object>(obj); // 軟引用
obj = null; // 銷毀強引用
      

執行個體:

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

public class Tess {

    public static class User {
        public Integer id;
        public String name;


        public User(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        // 建立對象,建立軟引用
        SoftReference<User> userSoftReference = new SoftReference<User>(new User(1, "zhangsan"));
        // 從軟引用中重新獲得強引用對象
        System.out.println(userSoftReference.get());

        System.gc();
        System.out.println("After GC:");
        // 垃圾回收之後獲得軟引用中的對象
        System.out.println(userSoftReference.get()); // 由于堆記憶體足夠,不會回收軟引用對象

        // 讓系統認為記憶體緊張(記憶體不夠時,會先進行GC)
        try {
            List list = new ArrayList();
            for (int i = 0; i < 100; i++) {
                byte[] b = new byte[1024 * 1024 * 600];
                list.add(b);
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            System.out.println(userSoftReference.get()); // 記憶體緊張,垃圾回收會将軟引用也回收,為null
        }
    }
}
      

弱引用(Weak Reference)——發現即回收

1.弱引用也是用來描述那些非必需對象,隻被弱引用關聯的對象隻能生存到下一次垃圾收集發生為止。在系統GC時,隻要發現弱引用,不管系統堆空間使用是否充足,都會回收掉隻被弱引用關聯的對象。

2.但是,由于垃圾回收器的線程通常優先級很低,是以,并不一定能很快地發現持有弱引用的對象。在這種情況下,弱引用對象可以存在較長的時間。

3.弱引用和軟引用一樣,在構造弱引用時,也可以指定一個引用隊列,當弱引用對象被回收時,就會加入指定的引用隊列,通過這個隊列可以跟蹤 對象的回收情況。

4.軟引用、弱引用都非常适合來儲存那些可有可無的緩存資料。如果這麼做,當系統記憶體不足時,這些緩存資料會被回收,不會導緻記憶體溢出。而當記憶體資源充足時,這些緩存資料又可以存在相當長的時間,進而起到加速系統的作用。

在JDK1.2版之後提供了java.lang.ref.WeakReference類來實作弱引用。

Object obj = new Object(); // 聲明強引用
WeakReference<Object> wr = new WeakReference<Object>(obj); // 聲明弱引用
obj = null; // 銷毀強引用
      

弱引用對象與軟引用對象的最大不同就在于,當GC在進行回收時,需要通過算法檢查是否回收軟引用對象,而對于弱引用對象,GC總是進行回收。弱引用對象更容易、更快被GC回收。

import java.lang.ref.WeakReference;

public class Tess {

    public static class User {
        public Integer id;
        public String name;

        public User(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        // 建立對象,建立軟引用
        WeakReference<User> userWeakReference = new WeakReference<User>(new User(1, "zhangsan"));
        // 從軟引用中重新獲得強引用對象
        System.out.println(userWeakReference.get());

        System.gc();
        System.out.println("After GC:");
        // 垃圾回收之後獲得弱引用中的對象
        System.out.println(userWeakReference.get()); // null
    }
}
      

面試題:你開發中使用過WeakHashMap嗎?

(緩存的Map)

虛引用(Phantom Reference)——對象回收跟蹤

1.也稱為“幽靈引用”或者“幻影引用”,是所有引用類型中最弱的一個。

2.一個對象是否有虛引用的存在,完全不會決定對象的生命周期。如果一個對象僅持有虛引用,那麼它和沒有引用幾乎是一樣的,随時都可能被垃圾回收器回收。

3.它不能單獨使用,也無法通過虛引用來擷取被引用的對象。當試圖通過虛引用的get()方法取得對象時,總是null。

4.為一個對象設定虛引用關聯的唯一目的在于跟蹤垃圾回收過程。比如:能在這個對象被回收時收到一個系統通知。

5.虛引用必須和引用隊列一起使用。虛引用在建立時必須提供一個引用隊列作為參數。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象後,将這個虛引用加入引用隊列,以通知應用程式對象的回收情況。

6.由于虛引用可以跟蹤對象的回收時間,是以,也可以将一些資源釋放操作放置在虛引用中執行和記錄。

7.JDK1.2版本之後提供了PhantomReference類來實作虛引用。

Object obj = new Object();
ReferenceQueue phantomQueue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, phantomQueue);
obj = null;
      
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class Tess {
    public static Tess obj; // 目前類對象的聲明
    static ReferenceQueue<Tess> phantomQueue = null; // 引用隊列

    public static class CheckRefQueue extends Thread {

        @Override
        public void run() {
            // 操作引用隊列
            while(true) {
                if(phantomQueue != null){
                    PhantomReference<Tess> objt = null;
                    try {
                        objt = (PhantomReference<Tess>) phantomQueue.remove(); // 去除最近的虛引用
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if(objt != null){
                        System.out.println("追蹤垃圾回收過程:Tess執行個體被GC了");
                    }
                }
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        // finalize()方法隻能被調用一次
        super.finalize();
        System.out.println("調用目前類的finalize()方法");
        obj = this; // 複活該對象
    }

    public static void main(String[] args) {

        Thread t = new CheckRefQueue();
        t.setDaemon(true); // 設定為守護線程(程式中沒有非守護線程時,程式結束)
        t.start();

        phantomQueue = new ReferenceQueue<Tess>();
        obj = new Tess();
        // 構造了Tess對象的虛引用,并制定了引用隊列
        PhantomReference<Tess> phantomReference = new PhantomReference<Tess>(obj, phantomQueue);
        try {
            // 不可擷取虛引用中的對象(null)
            System.out.println(phantomReference.get());

            // 将強引用去除
            obj = null;
            //第一次GC,由于對象複活,GC無法回收該對象
            System.gc();
            Thread.sleep(1000);
            if(obj == null){
                System.out.println("obj 為 null");
            } else {
                System.out.println("obj 可用");
            }
            System.out.println("第二次GC");
            obj = null;
            System.gc(); // 一旦将obj對象回收,就會将此虛引用存放到引用隊列中
            Thread.sleep(1000);
            if(obj == null){
                System.out.println("obj 為 null");
            } else {
                System.out.println("obj 可用");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
      

終結器引用(Final reference)

1.它用以實作對象的finalize()方法,也可以成為終接器引用。