日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

我們都知道 JVM 垃圾回收中,GC判斷堆中的對(duì)象實(shí)例或數(shù)據(jù)是不是垃圾的方法有引用計(jì)數(shù)法可達(dá)性算法兩種。

無(wú)論是通過(guò)引用計(jì)數(shù)算法判斷對(duì)象的引用數(shù)量,還是通過(guò)根搜索算法判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá),判定對(duì)象是否存活都與“引用”有關(guān)。

引用

先說(shuō)說(shuō)引用,JAVA中的引用,類似 C 語(yǔ)言中的指針。初學(xué) Java時(shí),我們就知道 Java 數(shù)據(jù)類型分兩大類,基本類型和引用類型。

基本類型:編程語(yǔ)言中內(nèi)置的最小粒度的數(shù)據(jù)類型。它包括四大類八種類型:

4種整數(shù)類型:byte、short、int、long

2種浮點(diǎn)數(shù)類型:float、double

1種字符類型:char

1種布爾類型:boolean

引用類型:引用類型指向一個(gè)對(duì)象,不是原始值,指向?qū)ο蟮淖兞渴且米兞?。?Java 里,除了基本類型,其他類型都屬于引用類型,它主要包括:類、接口、數(shù)組、枚舉、注解

有了數(shù)據(jù)類型,JVM對(duì)程序數(shù)據(jù)的管理就規(guī)范化了,不同的數(shù)據(jù)類型,它的存儲(chǔ)形式和位置是不一樣的

怎么跑偏了,回歸正題,通過(guò)引用,可以對(duì)堆中的對(duì)象進(jìn)行操作。引用《Java編程思想》中的一段話,

”每種編程語(yǔ)言都有自己的數(shù)據(jù)處理方式。有些時(shí)候,程序員必須注意將要處理的數(shù)據(jù)是什么類型。你是直接操縱元素,還是用某種基于特殊語(yǔ)法的間接表示(例如C/C++里的指針)來(lái)操作對(duì)象。所有這些在 Java 里都得到了簡(jiǎn)化,一切都被視為對(duì)象。因此,我們可采用一種統(tǒng)一的語(yǔ)法。盡管將一切都“看作”對(duì)象,但操縱的標(biāo)識(shí)符實(shí)際是指向一個(gè)對(duì)象的“引用”(reference)。”

比如:

Person person = new Person("張三");

這里的 person 就是指向Person 實(shí)例“張三”的引用,我們一般都是通過(guò) person 來(lái)操作“張三”實(shí)例。

在 JDK 1.2 之前,Java 中的引用的定義很傳統(tǒng):如果 reference 類型的數(shù)據(jù)中存儲(chǔ)的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱該 refrence 數(shù)據(jù)是代表某塊內(nèi)存、某個(gè)對(duì)象的引用。這種定義很純粹,但是太過(guò)狹隘,一個(gè)對(duì)象在這種定義下只有被引用或者沒(méi)有被引用兩種狀態(tài),對(duì)于如何描述一些“食之無(wú)味,棄之可惜”的對(duì)象就顯得無(wú)能為力。

比如我們希望能描述這樣一類對(duì)象:當(dāng)內(nèi)存空間還足夠時(shí),則能保留在內(nèi)存之中;如果內(nèi)存在進(jìn)行垃圾收集后還是非常緊張,則可以拋棄這些對(duì)象。很多系統(tǒng)的緩存功能都符合這樣的應(yīng)用場(chǎng)景。

在 JDK 1.2 之后,Java 對(duì)引用的概念進(jìn)行了擴(kuò)充,將引用分為

  • 強(qiáng)引用(Strong Reference)
  • 軟引用(Soft Reference)
  • 弱引用(Weak Reference)
  • 虛引用(Phantom Reference)

這四種引用強(qiáng)度依次逐漸減弱。

Java 中引入四種引用的目的是讓程序自己決定對(duì)象的生命周期,JVM 是通過(guò)垃圾回收器對(duì)這四種引用做不同的處理,來(lái)實(shí)現(xiàn)對(duì)象生命周期的改變。

JDK 8中的 UML關(guān)系圖

阿里面試:說(shuō)說(shuō)強(qiáng)引用、軟引用、弱引用、虛引用吧

 

FinalReference 類是包內(nèi)可見,其他三種引用類型均為 public,可以在應(yīng)用程序中直接使用。

強(qiáng)引用

在 Java 中最常見的就是強(qiáng)引用,把一個(gè)對(duì)象賦給一個(gè)引用變量,這個(gè)引用變量就是一個(gè)強(qiáng)引用。類似 “Object obj = new Object()” 這類的引用。

當(dāng)一個(gè)對(duì)象被強(qiáng)引用變量引用時(shí),它處于可達(dá)狀態(tài),是不可能被垃圾回收器回收的,即使該對(duì)象永遠(yuǎn)不會(huì)被用到也不會(huì)被回收。

當(dāng)內(nèi)存不足,JVM 開始垃圾回收,對(duì)于強(qiáng)引用的對(duì)象,就算是出現(xiàn)了 OOM 也不會(huì)對(duì)該對(duì)象進(jìn)行回收,打死都不收。因此強(qiáng)引用有時(shí)也是造成 Java 內(nèi)存泄露的原因之一。

對(duì)于一個(gè)普通的對(duì)象,如果沒(méi)有其他的引用關(guān)系,只要超過(guò)了引用的作用域或者顯示地將相應(yīng)(強(qiáng))引用賦值為 null,一般認(rèn)為就是可以被垃圾收集器回收。(具體回收時(shí)機(jī)還要要看垃圾收集策略)。

coding~

public class StrongRefenenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = o1;
        o1 = null;
        System.gc();
        System.out.println(o1);  //null
        System.out.println(o2);  //java.lang.Object@2503dbd3
    }
}

demo 中盡管 o1已經(jīng)被回收,但是 o2 強(qiáng)引用 o1,一直存在,所以不會(huì)被GC回收

軟引用

軟引用是一種相對(duì)強(qiáng)引用弱化了一些的引用,需要用java.lang.ref.SoftReference 類來(lái)實(shí)現(xiàn),可以讓對(duì)象豁免一些垃圾收集。

軟引用用來(lái)描述一些還有用,但并非必需的對(duì)象。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中并進(jìn)行第二次回收。如果這次回收還是沒(méi)有足夠的內(nèi)存,才會(huì)拋出內(nèi)存溢出異常。

對(duì)于只有軟引用的對(duì)象來(lái)說(shuō):當(dāng)系統(tǒng)內(nèi)存充足時(shí)它不會(huì)被回收,當(dāng)系統(tǒng)內(nèi)存不足時(shí)它才會(huì)被回收。

coding~

//VM options: -Xms5m -Xmx5m
public class SoftRefenenceDemo {

    public static void main(String[] args) {
        softRefMemoryEnough();
        System.out.println("------內(nèi)存不夠用的情況------");
        softRefMemoryNotEnough();
    }

    private static void softRefMemoryEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);
        System.out.println(s1.get());

        o1 = null;
        System.gc();

        System.out.println(o1);
        System.out.println(s1.get());
    }

     /**
     * JVM配置`-Xms5m -Xmx5m` ,然后故意new一個(gè)一個(gè)大對(duì)象,使內(nèi)存不足產(chǎn)生 OOM,看軟引用回收情況
     */
    private static void softRefMemoryNotEnough() {
        Object o1 = new Object();
        SoftReference<Object> s1 = new SoftReference<Object>(o1);
        System.out.println(o1);
        System.out.println(s1.get());

        o1 = null;

        byte[] bytes = new byte[10 * 1024 * 1024];

        System.out.println(o1);
        System.out.println(s1.get());
    }
}

Output

java.lang.Object@2503dbd3
java.lang.Object@2503dbd3
null
java.lang.Object@2503dbd3
------內(nèi)存不夠用的情況------
java.lang.Object@4b67cf4d
java.lang.Object@4b67cf4d
java.lang.OutOfMemoryError: Java heap space
	at reference.SoftRefenenceDemo.softRefMemoryNotEnough(SoftRefenenceDemo.java:42)
	at reference.SoftRefenenceDemo.main(SoftRefenenceDemo.java:15)
null
null

軟引用通常用在對(duì)內(nèi)存敏感的程序中,比如高速緩存就有用到軟引用,內(nèi)存夠用的時(shí)候就保留,不夠用就回收。

我們看下 Mybatis 緩存類 SoftCache 用到的軟引用

public Object getObject(Object key) {
    Object result = null;
    SoftReference<Object> softReference = (SoftReference)this.delegate.getObject(key);
    if (softReference != null) {
        result = softReference.get();
        if (result == null) {
            this.delegate.removeObject(key);
        } else {
            synchronized(this.hardLinksToAvoidGarbageCollection) {
                this.hardLinksToAvoidGarbageCollection.addFirst(result);
                if (this.hardLinksToAvoidGarbageCollection.size() > this.numberOfHardLinks) {
                    this.hardLinksToAvoidGarbageCollection.removeLast();
                }
            }
        }
    }
    return result;
}

弱引用

弱引用也是用來(lái)描述非必需對(duì)象的,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。

弱引用需要用java.lang.ref.WeakReference類來(lái)實(shí)現(xiàn),它比軟引用的生存期更短。

對(duì)于只有軟引用的對(duì)象來(lái)說(shuō),只要垃圾回收機(jī)制一運(yùn)行,不管 JVM 的內(nèi)存空間是否足夠,都會(huì)回收該對(duì)象占用的內(nèi)存。

coding~

public class WeakReferenceDemo {

    public static void main(String[] args) {
        Object o1 = new Object();
        WeakReference<Object> w1 = new WeakReference<Object>(o1);

        System.out.println(o1);
        System.out.println(w1.get());

        o1 = null;
        System.gc();

        System.out.println(o1);
        System.out.println(w1.get());
    }
}

Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mAppings.

官方文檔這么寫的,弱引用常被用來(lái)實(shí)現(xiàn)規(guī)范化映射,JDK 中的 WeakHashMap 就是一個(gè)這樣的例子

面試官:既然你都知道弱引用,那能說(shuō)說(shuō) WeakHashMap 嗎

public class WeakHashMapDemo {

    public static void main(String[] args) throws InterruptedException {
        myHashMap();
        myWeakHashMap();
    }

    public static void myHashMap() {
        HashMap<String, String> map = new HashMap<String, String>();
        String key = new String("k1");
        String value = "v1";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.gc();

        System.out.println(map);
    }

    public static void myWeakHashMap() throws InterruptedException {
        WeakHashMap<String, String> map = new WeakHashMap<String, String>();
        //String key = "weak";
        // 剛開始寫成了上邊的代碼
        //思考一下,寫成上邊那樣會(huì)怎么樣? 那可不是引用了
        String key = new String("weak");
        String value = "map";
        map.put(key, value);
        System.out.println(map);
        //去掉強(qiáng)引用
        key = null;
        System.gc();
        Thread.sleep(1000);
        System.out.println(map);
    }
}

我們看下 ThreadLocal 中用到的弱引用

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    //......
}

虛引用

虛引用也稱為“幽靈引用”或者“幻影引用”,它是最弱的一種引用關(guān)系。

虛引用,顧名思義,就是形同虛設(shè),與其他幾種引用都不太一樣,一個(gè)對(duì)象是否有虛引用的存在,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例。

虛引用需要java.lang.ref.PhantomReference 來(lái)實(shí)現(xiàn)。

如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收,它不能單獨(dú)使用也不能通過(guò)它訪問(wèn)對(duì)象,虛引用必須和引用隊(duì)列(RefenenceQueue)聯(lián)合使用。

虛引用的主要作用是跟蹤對(duì)象垃圾回收的狀態(tài)。僅僅是提供了一種確保對(duì)象被 finalize 以后,做某些事情的機(jī)制。

PhantomReference 的 get 方法總是返回 null,因此無(wú)法訪問(wèn)對(duì)應(yīng)的引用對(duì)象。其意義在于說(shuō)明一個(gè)對(duì)象已經(jīng)進(jìn)入 finalization 階段,可以被 GC 回收,用來(lái)實(shí)現(xiàn)比 finalization 機(jī)制更靈活的回收操作。

換句話說(shuō),設(shè)置虛引用的唯一目的,就是在這個(gè)對(duì)象被回收器回收的時(shí)候收到一個(gè)系統(tǒng)通知或者后續(xù)添加進(jìn)一步的處理。

Java 允許使用 finalize() 方法在垃圾收集器將對(duì)象從內(nèi)存中清除出去之前做必要的清理工作。

public class PhantomReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(o1,referenceQueue);

        System.out.println(o1);
        System.out.println(referenceQueue.poll());
        System.out.println(phantomReference.get());

        o1 = null;
        System.gc();
        Thread.sleep(3000);

        System.out.println(o1);
        System.out.println(referenceQueue.poll()); //引用隊(duì)列中
        System.out.println(phantomReference.get());
    }

}
java.lang.Object@4554617c
null
null
null
java.lang.ref.PhantomReference@74a14482
null

引用隊(duì)列

ReferenceQueue 是用來(lái)配合引用工作的,沒(méi)有ReferenceQueue 一樣可以運(yùn)行。

SoftReference、WeakReference、PhantomReference 都有一個(gè)可以傳遞 ReferenceQueue 的構(gòu)造器。

創(chuàng)建引用的時(shí)候,可以指定關(guān)聯(lián)的隊(duì)列,當(dāng) GC 釋放對(duì)象內(nèi)存的時(shí)候,會(huì)將引用加入到引用隊(duì)列。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng),這相當(dāng)于是一種通知機(jī)制。

當(dāng)關(guān)聯(lián)的引用隊(duì)列中有數(shù)據(jù)的時(shí)候,意味著指向的堆內(nèi)存中的對(duì)象被回收。通過(guò)這種方式,JVM 允許我們?cè)趯?duì)象被銷毀后,做一些我們自己想做的事情。

阿里面試:說(shuō)說(shuō)強(qiáng)引用、軟引用、弱引用、虛引用吧

 

最后,稍微了解下源碼中的實(shí)現(xiàn)

Reference源碼(JDK8)

強(qiáng)軟弱虛四種引用,我們有了個(gè)大概的認(rèn)識(shí),我們也知道除了強(qiáng)引用沒(méi)有對(duì)應(yīng)的類型表示,是普遍存在的。剩下的三種引用都是 java.lang.ref.Reference 的直接子類。

那就會(huì)有疑問(wèn)了,我們可以通過(guò)繼承 Reference,自定義引用類型嗎?

Abstract base class for reference objects. This class defines the operations common to all reference objects. Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.

JDK 官方文檔是這么說(shuō)的,Reference是所有引用對(duì)象的基類。這個(gè)類定義了所有引用對(duì)象的通用操作。因?yàn)橐脤?duì)象是與垃圾收集器緊密協(xié)作而實(shí)現(xiàn)的,所以這個(gè)類可能不能直接子類化。

Reference 的4種狀態(tài)

  • Active:新創(chuàng)建的引用實(shí)例處于Active狀態(tài),但當(dāng)GC檢測(cè)到該實(shí)例引用的實(shí)際對(duì)象的可達(dá)性發(fā)生某些改變(實(shí)際對(duì)象處于 GC roots 不可達(dá))后,它的狀態(tài)將變化為Pending或者Inactive。如果 Reference 注冊(cè)了ReferenceQueue,則會(huì)切換為Pending,并且Reference會(huì)加入pending-Reference鏈表中,如果沒(méi)有注冊(cè)ReferenceQueue,會(huì)切換為Inactive。
  • Pending:當(dāng)引用實(shí)例被放置在pending-Reference 鏈表中時(shí),它處于Pending狀態(tài)。此時(shí),該實(shí)例在等待一個(gè)叫Reference-handler的線程將此實(shí)例進(jìn)行enqueue操作。如果某個(gè)引用實(shí)例沒(méi)有注冊(cè)在一個(gè)引用隊(duì)列中,該實(shí)例將永遠(yuǎn)不會(huì)進(jìn)入Pending狀態(tài)
  • Enqueued:在ReferenceQueue隊(duì)列中的Reference的狀態(tài),如果Reference從隊(duì)列中移除,會(huì)進(jìn)入Inactive狀態(tài)
  • Inactive:一旦某個(gè)引用實(shí)例處于Inactive狀態(tài),它的狀態(tài)將不再會(huì)發(fā)生改變,同時(shí)說(shuō)明該引用實(shí)例所指向的實(shí)際對(duì)象一定會(huì)被GC所回收
阿里面試:說(shuō)說(shuō)強(qiáng)引用、軟引用、弱引用、虛引用吧

 

Reference的構(gòu)造函數(shù)和成員變量

public abstract class Reference<T> {
   //引用指向的對(duì)象
   private T referent;    
   // reference被回收后,當(dāng)前Reference實(shí)例會(huì)被添加到這個(gè)隊(duì)列中
   volatile ReferenceQueue<? super T> queue;
   //下一個(gè)Reference實(shí)例的引用,Reference實(shí)例通過(guò)此構(gòu)造單向的鏈表
   volatile Reference next;
   //由transient修飾,基于狀態(tài)表示不同鏈表中的下一個(gè)待處理的對(duì)象,主要是pending-reference列表的下一個(gè)元素,通過(guò)JVM直接調(diào)用賦值
   private transient Reference<T> discovered;
   // 等待加入隊(duì)列的引用列表,這里明明是個(gè)Reference類型的對(duì)象,官方文檔確說(shuō)是個(gè)list?
   //因?yàn)镚C檢測(cè)到某個(gè)引用實(shí)例指向的實(shí)際對(duì)象不可達(dá)后,會(huì)將該pending指向該引用實(shí)例,
   //discovered字段則是用來(lái)表示下一個(gè)需要被處理的實(shí)例,因此我們只要不斷地在處理完當(dāng)前pending之后,將discovered指向的實(shí)例賦予給pending即可。所以這個(gè)pending就相當(dāng)于是一個(gè)鏈表。
   private static Reference<Object> pending = null;
    
    /* -- Constructors -- */
    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
}

Reference 提供了兩個(gè)構(gòu)造器,一個(gè)帶引用隊(duì)列 ReferenceQueue,一個(gè)不帶。

帶 ReferenceQueue 的意義在于我們可以從外部通過(guò)對(duì) ReferenceQueue 的操作來(lái)了解到引用實(shí)例所指向的實(shí)際對(duì)象是否被回收了,同時(shí)我們也可以通過(guò) ReferenceQueue 對(duì)引用實(shí)例進(jìn)行一些額外的操作;但如果我們的引用實(shí)例在創(chuàng)建時(shí)沒(méi)有指定一個(gè)引用隊(duì)列,那我們要想知道實(shí)際對(duì)象是否被回收,就只能夠不停地輪詢引用實(shí)例的get() 方法是否為空了。

值得注意的是虛引用 PhantomReference,由于它的 get() 方法永遠(yuǎn)返回 null,因此它的構(gòu)造函數(shù)必須指定一個(gè)引用隊(duì)列。

這兩種查詢實(shí)際對(duì)象是否被回收的方法都有應(yīng)用,如 WeakHashMap 中就選擇去查詢 queue 的數(shù)據(jù),來(lái)判定是否有對(duì)象將被回收;而 ThreadLocalMap,則采用判斷 get() 是否為 null 來(lái)作處理。

實(shí)例方法(和ReferenceHandler線程不相關(guān)的方法)

private static Lock lock = new Lock();
// 獲取持有的referent實(shí)例
public T get() {
    return this.referent;
}
// 把持有的referent實(shí)例置為null
public void clear() {
    this.referent = null;
}
// 判斷是否處于enqeued狀態(tài)
public boolean isEnqueued() {
    return (this.queue == ReferenceQueue.ENQUEUED);
}
// 入隊(duì)參數(shù),同時(shí)會(huì)把referent置為null
public boolean enqueue() {
    return this.queue.enqueue(this);
}

ReferenceHandler線程

通過(guò)上文的討論,我們知道一個(gè)Reference實(shí)例化后狀態(tài)為Active,其引用的對(duì)象被回收后,垃圾回收器將其加入到pending-Reference鏈表,等待加入ReferenceQueue。

ReferenceHandler線程是由Reference靜態(tài)代碼塊中建立并且運(yùn)行的線程,它的運(yùn)行方法中依賴了比較多的本地(native)方法,ReferenceHandler線程的主要功能就pending list中的引用實(shí)例添加到引用隊(duì)列中,并將pending指向下一個(gè)引用實(shí)例。

// 控制垃圾回收器操作與Pending狀態(tài)的Reference入隊(duì)操作不沖突執(zhí)行的全局鎖
// 垃圾回收器開始一輪垃圾回收前要獲取此鎖
// 所以所有占用這個(gè)鎖的代碼必須盡快完成,不能生成新對(duì)象,也不能調(diào)用用戶代碼
static private class Lock { }
private static Lock lock = new Lock();

private static class ReferenceHandler extends Thread {

    private static void ensureClassInitialized(Class<?> clazz) {
        try {
            Class.forName(clazz.getName(), true, clazz.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
        }
    }

    static {
        ensureClassInitialized(InterruptedException.class);
        ensureClassInitialized(Cleaner.class);
    }

    ReferenceHandler(ThreadGroup g, String name) {
        super(g, name);
    }

    public void run() {
        while (true) {
            tryHandlePending(true);
        }
    }
}

static boolean tryHandlePending(boolean waitForNotify) {
    Reference<Object> r;
    Cleaner c;
    try {
        synchronized (lock) {
            // 判斷pending-Reference鏈表是否有數(shù)據(jù)
            if (pending != null) {
                // 如果有Pending Reference,從列表中取出
                r = pending;
                c = r instanceof Cleaner ? (Cleaner) r : null;
                // unlink 'r' from 'pending' chain
                pending = r.discovered;
                r.discovered = null;
            } else {
    // 如果沒(méi)有Pending Reference,調(diào)用wait等待
                if (waitForNotify) {
                    lock.wait();
                }
                // retry if waited
                return waitForNotify;
            }
        }
    } catch (OutOfMemoryError x) {
        Thread.yield();
        return true;
    } catch (InterruptedException x) {
        return true;
    }

    // Fast path for cleaners
    if (c != null) {
        c.clean();
        return true;
    }

    ReferenceQueue<? super Object> q = r.queue;
    if (q != ReferenceQueue.NULL) q.enqueue(r);
    return true;
}

//ReferenceHandler線程是在Reference的static塊中啟動(dòng)的
static {
    // ThreadGroup繼承當(dāng)前執(zhí)行線程(一般是主線程)的線程組
    ThreadGroup tg = Thread.currentThread().getThreadGroup();
    for (ThreadGroup tgn = tg;
         tgn != null;
         tg = tgn, tgn = tg.getParent());
    // 創(chuàng)建線程實(shí)例,命名為Reference Handler,配置最高優(yōu)先級(jí)和后臺(tái)運(yùn)行(守護(hù)線程),然后啟動(dòng)
    Thread handler = new ReferenceHandler(tg, "Reference Handler");
    // ReferenceHandler線程有最高優(yōu)先級(jí)
    handler.setPriority(Thread.MAX_PRIORITY);
    handler.setDaemon(true);
    handler.start();

    // provide access in SharedSecrets
    SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
        @Override
        public boolean tryHandlePendingReference() {
            return tryHandlePending(false);
        }
    });
}

由于ReferenceHandler線程是Reference的靜態(tài)代碼創(chuàng)建的,所以只要Reference這個(gè)父類被初始化,該線程就會(huì)創(chuàng)建和運(yùn)行,由于它是守護(hù)線程,除非 JVM 進(jìn)程終結(jié),否則它會(huì)一直在后臺(tái)運(yùn)行(注意它的run()方法里面使用了死循環(huán))。

ReferenceQueue源碼

public class ReferenceQueue<T> {

    public ReferenceQueue() { }
 // 內(nèi)部類Null類繼承自ReferenceQueue,覆蓋了enqueue方法返回false
    private static class Null<S> extends ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }
  // 用于標(biāo)識(shí)沒(méi)有注冊(cè)Queue
    static ReferenceQueue<Object> NULL = new Null<>();
    // 用于標(biāo)識(shí)已經(jīng)處于對(duì)應(yīng)的Queue中
    static ReferenceQueue<Object> ENQUEUED = new Null<>();

    // 靜態(tài)內(nèi)部類,作為鎖對(duì)象
    static private class Lock { };
    /* 互斥鎖,用于同步ReferenceHandler的enqueue和用戶線程操作的remove和poll出隊(duì)操作 */
    private Lock lock = new Lock();
    // 引用鏈表的頭節(jié)點(diǎn)
    private volatile Reference<? extends T> head = null;
    // 引用隊(duì)列長(zhǎng)度,入隊(duì)則增加1,出隊(duì)則減少1
    private long queueLength = 0;

    // 入隊(duì)操作,只會(huì)被Reference實(shí)例調(diào)用
    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
   // 如果引用實(shí)例持有的隊(duì)列為ReferenceQueue.NULL或者ReferenceQueue.ENQUEUED則入隊(duì)失敗返回false
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;
            // 當(dāng)前引用實(shí)例已經(jīng)入隊(duì),那么它本身持有的引用隊(duì)列實(shí)例置為ReferenceQueue.ENQUEUED
            r.queue = ENQUEUED;
            // 如果鏈表沒(méi)有元素,則此引用實(shí)例直接作為頭節(jié)點(diǎn),否則把前一個(gè)引用實(shí)例作為下一個(gè)節(jié)點(diǎn)
            r.next = (head == null) ? r : head;
            // 當(dāng)前實(shí)例更新為頭節(jié)點(diǎn),也就是每一個(gè)新入隊(duì)的引用實(shí)例都是作為頭節(jié)點(diǎn),已有的引用實(shí)例會(huì)作為后繼節(jié)點(diǎn)
            head = r;
            // 隊(duì)列長(zhǎng)度增加1
            queueLength++;
            // 特殊處理FinalReference,VM進(jìn)行計(jì)數(shù)
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            // 喚醒所有等待的線程
            lock.notifyAll();
            return true;
        }
    }

    // 引用隊(duì)列的poll操作,此方法必須在加鎖情況下調(diào)用
    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        if (r != null) {
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            // 更新next節(jié)點(diǎn)為頭節(jié)點(diǎn),如果next節(jié)點(diǎn)為自身,說(shuō)明已經(jīng)走過(guò)一次出隊(duì),則返回null
            head = (rn == r) ? null : rn;
            r.queue = NULL;
            // 當(dāng)前頭節(jié)點(diǎn)變更為環(huán)狀隊(duì)列,考慮到FinalReference尚為inactive和避免重復(fù)出隊(duì)的問(wèn)題
            r.next = r;
            // 隊(duì)列長(zhǎng)度減少1
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }

    // 隊(duì)列的公有poll操作,主要是加鎖后調(diào)用reallyPoll
    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }
// 移除引用隊(duì)列中的下一個(gè)引用元素,實(shí)際上也是依賴于reallyPoll的Object提供的阻塞機(jī)制
    public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            for (;;) {
                lock.wait(timeout);
                r = reallyPoll();
                if (r != null) return r;
                if (timeout != 0) {
                    long end = System.nanoTime();
                    timeout -= (end - start) / 1000_000;
                    if (timeout <= 0) return null;
                    start = end;
                }
            }
        }
    }

    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);
    }

    void forEach(Consumer<? super Reference<? extends T>> action) {
        for (Reference<? extends T> r = head; r != null;) {
            action.accept(r);
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            if (rn == r) {
                if (r.queue == ENQUEUED) {
                    // still enqueued -> we reached end of chain
                    r = null;
                } else {
                    // already dequeued: r.queue == NULL; ->
                    // restart from head when overtaken by queue poller(s)
                    r = head;
                }
            } else {
                // next in chain
                r = rn;
            }
        }
    }
}

ReferenceQueue只存儲(chǔ)了Reference鏈表的頭節(jié)點(diǎn),真正的Reference鏈表的所有節(jié)點(diǎn)是存儲(chǔ)在Reference實(shí)例本身,通過(guò)屬性 next 拼接的,ReferenceQueue提供了對(duì)Reference鏈表的入隊(duì)、poll、remove等操作

 

分享到:
標(biāo)簽:阿里 面試
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定