JAVA引用(WeakHashMap、Cleaner)

Expunge 刪除,抹去
stale   陳腐、老舊的

Reference 引用類

強(qiáng)引用、軟引用、弱引用、虛引用

軟引用、弱引用、虛引用,可以配合ReferenceQueue實(shí)現(xiàn)對象被回收時(shí)候的監(jiān)聽

Reference

重要屬性:

1. private T referent;       
weakHashMap中就是那個(gè)Entry的Key   Treated specially by GC

2. volatile ReferenceQueue<? super T> queue;
Reference隊(duì)列,GC后Reference對象會(huì)被加到這里面,是放到隊(duì)列頭部。

3. private static Lock lock = new Lock();
GC時(shí)候必須要獲得這個(gè)鎖

4. private static Reference<Object> pending = null;
跟虛擬機(jī),GC打交道的。 GC后Reference對象會(huì)被GC自動(dòng)設(shè)置到這個(gè)引用中,然后由ReferenceHandler線程把他放到ReferenceQueue里面.
    
5. transient private Reference<T> discovered; 
跟虛擬機(jī),GC打交道的  used by VM 

6. volatile Reference next;
ReferenceQueue是linkedList類似是實(shí)現(xiàn),鏈表結(jié)構(gòu),Reference就相當(dāng)于其中的Node,有一個(gè)next的指針指向下一個(gè)對象。

ReferenceHandler 內(nèi)部類:

  1. 在Reference類的靜態(tài)代碼塊中初始化,然后運(yùn)行。
  2. 主要功能是在tryHandlePending()方法中,把Reference類的靜態(tài)變量pending指向的Reference對象丟到他自己的queue中。
  3. 于此同時(shí),如果pending所指的Reference對象是Cleaner,那么還會(huì)執(zhí)行Cleaner的clean方法,而且不會(huì)放到queue中,執(zhí)行了clean方法后就返回了。

Cleaner在DirectByteBuffer中有使用,下面介紹.

Cleaner類

public class Cleaner extends PhantomReference<Object> 集成虛引用, 也就是說cleaner的var0只要他強(qiáng)引用消失了,那么var0所指的對象就隨時(shí)會(huì)被GC掉。

//創(chuàng)建方法,add方法也是把cleaner自身構(gòu)建成一個(gè)鏈表結(jié)構(gòu)
public static Cleaner create(Object var0, Runnable var1) {
   return var1 == null ? null : add(new Cleaner(var0, var1));
}
private Cleaner(Object var1, Runnable var2) {
   super(var1, dummyQueue);
   this.thunk = var2;
}

clean方法,在Reference的tryHandlePending方法中會(huì)執(zhí)行,即Cleaner關(guān)聯(lián)的對象被GC放到pending中,然后ReferenceHandler線程執(zhí)行tryHandlePending時(shí)候執(zhí)行。

    public void clean() {
        //刪除自身節(jié)點(diǎn)
        if (remove(this)) {
            try {
            //執(zhí)行創(chuàng)建時(shí)候傳進(jìn)來的runnable對象的run方法,執(zhí)行相關(guān)的業(yè)務(wù)邏輯
            //比如DirectByteBuffer類中,他有個(gè)cleaner,run方法是用于釋放直接內(nèi)存
            //詳見下
                this.thunk.run();
            } catch (final Throwable var2) {
                //省略。。。
            }
        }
    }

Cleaner在DirectByteBuffer中有使用,下面介紹.

DirectByteBuffer中Cleaner的使用

DirectByteBuffer基于unsafe類的allocateMemory來分配直接內(nèi)存使用。

基于Cleaner來進(jìn)行直接內(nèi)存的釋放。

直接內(nèi)存申請,釋放流程:
  1. new DirectByteBuffer(1024)構(gòu)建對象,構(gòu)造方法內(nèi)部調(diào)用Unsafe.allocateMemory申請到直接內(nèi)存(不歸JVM GC管轄)
  2. 構(gòu)造方法內(nèi)部還調(diào)用``Cleaner.create(this, new Deallocator(base, size, cap))`把當(dāng)前DirectByteBuffer設(shè)置為虛引用,并設(shè)置一個(gè)Deallocator的runnable類。
  3. 當(dāng)DirectByteBuffer強(qiáng)引用消失,即不可達(dá)之后,由于Cleaner是虛引用,不影響GC,所以DirectByteBuffer被GC掉了
  4. 與此同時(shí),Cleaner對象也被GC設(shè)置到Reference的靜態(tài)屬性pending中(這個(gè)時(shí)候Cleaner即Reference的Referent引用指向的DirectByteBuffer已經(jīng)是null了)
  5. Reference中的ReferenceHandler線程執(zhí)行tryHandlePending,處理pending,即該Cleaner
  6. tryHandlePending中調(diào)用Cleaner的clean方法,clean方法內(nèi)部調(diào)用構(gòu)建時(shí)候傳入的runnable對象的run方法,即Deallocator的run方法。
  7. Deallocator的run方法中調(diào)用了unsafe.freeMemory(address);來釋放DirectByteBuffer在構(gòu)建時(shí)候申請的直接內(nèi)存
  8. 以此來完成直接內(nèi)存的釋放

WeakHashMap

https://hongjiang.info/java-referencequeue/

以前設(shè)計(jì)緩存時(shí)也曾過用WeakHashMap來實(shí)現(xiàn),對Java的Reference稍做過一些了解,其實(shí)這個(gè)問題,歸根到底,是個(gè)Java GC的問題,由垃圾回收器與ReferenceQueue的交互方式?jīng)Q定的。WeakHashMap的實(shí)現(xiàn)也是通過ReferenceQueue這個(gè)“監(jiān)聽器”來優(yōu)雅的實(shí)現(xiàn)自動(dòng)刪除那些引用不可達(dá)的key的。

Entry類繼承了WeakReference, 其中的Key是弱引用,并且WeakHashMap中定義了一個(gè)ReferenceQueue,用于監(jiān)聽key的回收

  //Entry類繼承弱引用
  private static class Entry<K,V> extends WeakReference<Object> {}
  
  //WeakHashMap中的實(shí)例變量用于監(jiān)聽key的回收
  private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
流程
  1. 當(dāng)WeakHashMap中Key沒有其他強(qiáng)引用時(shí)候,發(fā)生了一次任意GC,由于Entry的Key是弱引用,那么被GC回收。

  2. 被GC回收的同時(shí),Entry類(繼承了弱引用)會(huì)由GC自動(dòng)放入到Reference的靜態(tài)屬性pending中。

  3. Reference類在類初始化后有個(gè)static代碼塊,里面啟動(dòng)了一個(gè)高優(yōu)先級的daemon線程,用于把pending指向的Reference對象放到ReferenceQueue中

  4. 經(jīng)過3后,Entry就被丟到了ReferenceQueue中

  5. 當(dāng)你在GC后調(diào)用WeakHashMap的get、put、size等方法時(shí)候,他會(huì)調(diào)用自己的expungeStaleEntries()方法。

  6. expungeStaleEntries方法會(huì)poll出queue中的Entry類,然后把entry類處理下,value=null,釋放掉value的強(qiáng)引用,然后處理前后的指針,size--之類。

  7. 經(jīng)過6之后,key對應(yīng)的value強(qiáng)引用也被抹去了,所以GC也可以把value以及整個(gè)entry回收掉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容