天天看點

Java中的強引用、軟引用、弱引用、虛引用詳解一 概念簡介二 涉及的類三 測試demo三 總結

一 概念簡介

在JVM記憶體中,一個對象擁有不同的引用類型,那這個對象在進行垃圾回收時會被執行不同的操作,進而影響這個對象的生命周期

1.1 強引用

強引用是使用最普遍的引用,我們平時代碼中定義的引用都是強引用。如果一個對象具有強引用,垃圾回收器絕不會回收它,即使是記憶體空間不足時,Java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不會靠随意回收具有強引用的對象來解決記憶體不足問題。

1.2 軟引用

如果一個對象隻具有軟引用,那就類似于可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些對象的記憶體。隻要垃圾回收器沒有回收它,該對象就可以被程式使用。軟引用一般用于實作緩存對象的操作

軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,Java虛拟機就會把這個軟引用加入到與之關聯的引用隊列中

1.3 弱引用

弱引用是比軟引用強度更低一種引用方式。如果一個對象隻具有弱引用,他的生命周期更加短暫,那麼當垃圾回收器線程在掃描他所管理的記憶體區域時,不管記憶體空間是否足夠,都會将這個對象回收。不過垃圾回收器線程優先級比較低,是以隻有弱引用的對象不一定會很快的被回收。

弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛拟機就會把這個弱引用加入到與之關聯的引用隊列中。

1.4 虛引用

虛引用又叫做幽靈引用,就想他的名字一樣,虛引用是形同虛設的,也就是虛引用相當于沒有引用。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。虛引用主要用于跟蹤一個對象被垃圾回收的過程。

虛引用必須和引用隊列(ReferenceQueue)聯合使用(因為虛引用無法獲得引用對象,不配合引用隊列使用的話将毫無意義)。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的記憶體之前,把這個虛引用加入到與之關聯的引用隊列中。程式可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否将要被垃圾回收。程式如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的記憶體被回收之前采取必要的行動。

二 涉及的類

在java.lang.ref包中提供了三個類:SoftReference類、WeakReference類和PhantomReference類,他們分别對應軟引用、弱引用和虛引用。這是三個類的父類都是Reference類,他們的方法基本上都一樣。

2.1 軟引用(SoftReference)

2.1.1 構造器

// 1.生成一個指向refrent對象的軟引用
public SoftReference(T referent)
// 2.使用引用隊列
public SoftReference(T referent, ReferenceQueue<? super T> q)
           

2.1.2 如何獲得引用對象

// 通過get方法得到軟引用所指向的對象
public T get() 
           
如果這個引用指向的對象已經被回收,那麼get( )将傳回null

2.2 弱引用(WeakReference)

2.2.1 構造器

// 1.生成一個指向refrent對象的弱引用
public WeakReference(T referent)
// 2.使用引用隊列
public WeakReference(T referent, ReferenceQueue<? super T> q)
           

2.2.2 如何獲得引用對象

// 通過get方法得到弱引用所指向的對象
public T get() 
           
如果這個引用指向的對象已經被回收,那麼get( )将傳回null

2.3 虛引用(PhantomReference)

2.3.1 構造器

// 使用引用隊列(虛引用必須配合引用隊列使用,否則沒有意義)
public WeakReference(T referent, ReferenceQueue<? super T> q)
           

2.3.2 如何獲得引用對象

// 通過get方法得到null?
public T get() {
        return null;
    }
           
虛引用不會獲得對象的引用,是以重載了Reference的get方法,直接傳回null。

2.4 引用隊列

public class ReferenceQueue<T> {
    public Reference<? extends T> poll()     //将隊列頂端的元素抛出
    public Reference<? extends T> remove()   // 将隊列元素删除
}
           

三 測試demo

3.1 測試程式

package com.example.demo.test;

import java.lang.ref.*;
import java.util.LinkedList;

public class ReferenceTest {

    private static ReferenceQueue<Source> rq = new ReferenceQueue<Source>();

    public static void checkQueue() {
        Reference<? extends Source> ref = null;
        while ((ref = rq.poll()) != null) {
            if (ref != null) {
                if(ref instanceof SourceSoftReference) {
                    System.out.println("In queue: " + ((SourceSoftReference) (ref)).id);
                } else if(ref instanceof SourceWeakReference) {
                    System.out.println("In queue: " + ((SourceWeakReference) (ref)).id);
                } else {
                    System.out.println("In queue: " + ((SourcePhantomReference) (ref)).id);
                }

            }
        }
    }

    public static void main(String args[]) {
//        soft();
//        weak();
        phantom();
    }
    public static void soft() {
        int size = ;
        LinkedList<SourceSoftReference> softList = new LinkedList<SourceSoftReference>();
        for (int i = ; i < size; i++) {
            softList.add(new SourceSoftReference(new Source("Soft " + i), rq));
            System.out.println("Just created soft: " + softList.getLast());

        }

        System.gc();
        try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
            Thread.currentThread().sleep();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = ; i < size; i++) {
            System.out.println("output: " + softList.get(i).get());

        }
        checkQueue();
    }
    public static void weak() {
        int size = ;
        LinkedList<SourceWeakReference> weakList = new LinkedList<SourceWeakReference>();
        for (int i = ; i < size; i++) {
            weakList.add(new SourceWeakReference(new Source("Weak " + i), rq));
            System.out.println("Just created weak: " + weakList.getLast());

        }

        System.gc();
        try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
            Thread.currentThread().sleep();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = ; i < size; i++) {
            System.out.println("output: " + weakList.get(i).get());

        }
        checkQueue();
    }

    public static void phantom() {
        int size = ;
        LinkedList<SourcePhantomReference> phantomList = new LinkedList<SourcePhantomReference>();
        for (int i = ; i < size; i++) {
            phantomList.add(new SourcePhantomReference(new Source("Phantom " + i), rq));
            System.out.println("Just created phantom: " + phantomList.getLast());

        }

        System.gc();
        try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
            Thread.currentThread().sleep();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        System.gc();
//        try { // 下面休息幾分鐘,讓上面的垃圾回收線程運作完成
//            Thread.currentThread().sleep(2000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        for (int i = ; i < size; i++) {
            System.out.println("output: " + phantomList.get(i).get());

        }
        checkQueue();
    }

}

class Source {
    public String id;

    public Source(String id) {
        this.id = id;
    }

    protected void finalize() {
        System.out.println("Finalizing Source " + id);
    }
}
class SourceSoftReference extends SoftReference<Source> {
    public String id;

    public SourceSoftReference(Source Source, ReferenceQueue<Source> rq) {
        super(Source, rq);
        this.id = Source.id;
    }
}

class SourceWeakReference extends WeakReference<Source> {
    public String id;

    public SourceWeakReference(Source Source, ReferenceQueue<Source> rq) {
        super(Source, rq);
        this.id = Source.id;
    }
}

class SourcePhantomReference extends PhantomReference<Source> {
    public String id;

    public SourcePhantomReference(Source Source, ReferenceQueue<Source> rq) {
        super(Source, rq);
        this.id = Source.id;
    }

}
           

3.2 分析

3.2.1 運作soft()方法

輸出:

Just created soft: [email protected]

Just created soft: [email protected]

Just created soft: [email protected]

output: [email protected]

output: [email protected]

output: [email protected]

軟引用仍然能獲得對象的引用,且引用隊列為空,說明記憶體充足,對象未被回收

3.2.2 運作weak()方法

Just created weak: [email protected]

Just created weak: [email protected]

Just created weak: [email protected]

Finalizing Source Weak 2

Finalizing Source Weak 1

Finalizing Source Weak 0

output: null

output: null

output: null

In queue: Weak 1

In queue: Weak 2

In queue: Weak 0

弱引用無法獲得對象的引用,且引用隊列中有3個弱引用,說明弱引用指向的對象已經被回收

3.2.2 運作phantom()方法

Just created phantom: [email protected]

Just created phantom: [email protected]

Just created phantom: [email protected]

Finalizing Source Phantom 2

Finalizing Source Phantom 1

Finalizing Source Phantom 0

output: null

output: null

output: null

虛引用無法獲得對象的引用屬于正常現象,但是引用隊列為空不正常。這是為什麼呢?

問題出在了source對象的finalize方法上,注釋掉finalize方法,再運作結果如下:

Just created phantom: [email protected]

Just created phantom: [email protected]

Just created phantom: [email protected]

output: null

output: null

output: null

In queue: Phantom 2

In queue: Phantom 0

In queue: Phantom 1

這是為什麼???????

原因簡單叙述如下:

在垃圾回收時:

弱引用:一旦探測對象隻有弱引用,就會被插入到ReferenceQueue

虛引用:隻有對象确實被GC銷毀,才會被插入到ReferenceQueue

也就是說上例中虛引用指向的對象并沒有被銷毀

為什麼執行了 System.gc()後對象沒有被銷毀??

因為對象重載了finalize方法,需要執行2輪gc才能回收。

如何修改

把phantom()方法的注釋解開,執行2次GC後,輸出如我們想象中一樣

Just created phantom: [email protected]

Just created phantom: [email protected]

Just created phantom: [email protected]

Finalizing Source Phantom 0

Finalizing Source Phantom 2

Finalizing Source Phantom 1

output: null

output: null

output: null

In queue: Phantom 1

In queue: Phantom 0

In queue: Phantom 2

三 總結

不知道大家發現沒有,通過虛引用的demo,我們不難發現,弱引用的demo中的Source對象也未必被回收掉了(一輪gc肯定不會被回收掉)。

是以,慎重重寫finalize方法,它會延緩對象被銷毀的速度。