Expunge 刪除哥纫,抹去
stale 陳腐霉旗、老舊的
Reference 引用類
強引用、軟引用蛀骇、弱引用厌秒、虛引用
軟引用、弱引用擅憔、虛引用鸵闪,可以配合ReferenceQueue實現對象被回收時候的監(jiān)聽
Reference
重要屬性:
1. private T referent;
weakHashMap中就是那個Entry的Key Treated specially by GC
2. volatile ReferenceQueue<? super T> queue;
Reference隊列,GC后Reference對象會被加到這里面暑诸,是放到隊列頭部蚌讼。
3. private static Lock lock = new Lock();
GC時候必須要獲得這個鎖
4. private static Reference<Object> pending = null;
跟虛擬機,GC打交道的个榕。 GC后Reference對象會被GC自動設置到這個引用中篡石,然后由ReferenceHandler線程把他放到ReferenceQueue里面.
5. transient private Reference<T> discovered;
跟虛擬機,GC打交道的 used by VM
6. volatile Reference next;
ReferenceQueue是linkedList類似是實現西采,鏈表結構凰萨,Reference就相當于其中的Node,有一個next的指針指向下一個對象沟蔑。
ReferenceHandler 內部類:
- 在Reference類的靜態(tài)代碼塊中初始化,然后運行狱杰。
- 主要功能是在tryHandlePending()方法中瘦材,把Reference類的靜態(tài)變量pending指向的Reference對象丟到他自己的queue中。
- 于此同時仿畸,如果pending所指的Reference對象是Cleaner食棕,那么還會執(zhí)行Cleaner的clean方法朗和,而且不會放到queue中,執(zhí)行了clean方法后就返回了簿晓。
Cleaner在DirectByteBuffer中有使用眶拉,下面介紹.
Cleaner類
public class Cleaner extends PhantomReference<Object>
集成虛引用, 也就是說cleaner的var0只要他強引用消失了,那么var0所指的對象就隨時會被GC掉憔儿。
//創(chuàng)建方法忆植,add方法也是把cleaner自身構建成一個鏈表結構
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方法中會執(zhí)行谒臼,即Cleaner關聯的對象被GC放到pending中朝刊,然后ReferenceHandler線程執(zhí)行tryHandlePending時候執(zhí)行。
public void clean() {
//刪除自身節(jié)點
if (remove(this)) {
try {
//執(zhí)行創(chuàng)建時候傳進來的runnable對象的run方法蜈缤,執(zhí)行相關的業(yè)務邏輯
//比如DirectByteBuffer類中拾氓,他有個cleaner,run方法是用于釋放直接內存
//詳見下
this.thunk.run();
} catch (final Throwable var2) {
//省略底哥。咙鞍。。
}
}
}
Cleaner在DirectByteBuffer中有使用趾徽,下面介紹.
DirectByteBuffer中Cleaner的使用
DirectByteBuffer基于unsafe類的allocateMemory來分配直接內存使用续滋。
基于Cleaner來進行直接內存的釋放。
直接內存申請附较,釋放流程:
-
new DirectByteBuffer(1024)
構建對象吃粒,構造方法內部調用Unsafe.allocateMemory申請到直接內存(不歸JVM GC管轄) - 構造方法內部還調用``Cleaner.create(this, new Deallocator(base, size, cap))`把當前DirectByteBuffer設置為虛引用潦俺,并設置一個Deallocator的runnable類拒课。
- 當DirectByteBuffer強引用消失,即不可達之后事示,由于Cleaner是虛引用早像,不影響GC,所以DirectByteBuffer被GC掉了
- 與此同時肖爵,Cleaner對象也被GC設置到Reference的靜態(tài)屬性pending中(這個時候Cleaner即Reference的Referent引用指向的DirectByteBuffer已經是null了)
- Reference中的ReferenceHandler線程執(zhí)行tryHandlePending卢鹦,處理pending,即該Cleaner
- tryHandlePending中調用Cleaner的clean方法劝堪,clean方法內部調用構建時候傳入的runnable對象的run方法冀自,即Deallocator的run方法。
- Deallocator的run方法中調用了
unsafe.freeMemory(address);
來釋放DirectByteBuffer在構建時候申請的直接內存 - 以此來完成直接內存的釋放
WeakHashMap
https://hongjiang.info/java-referencequeue/
以前設計緩存時也曾過用WeakHashMap來實現秒啦,對Java的Reference
稍做過一些了解熬粗,其實這個問題,歸根到底余境,是個Java GC
的問題驻呐,由垃圾回收器與ReferenceQueue
的交互方式決定的灌诅。WeakHashMap
的實現也是通過ReferenceQueue
這個“監(jiān)聽器”來優(yōu)雅的實現自動刪除那些引用不可達的key的。
Entry類繼承了WeakReference, 其中的Key是弱引用含末,并且WeakHashMap中定義了一個ReferenceQueue猜拾,用于監(jiān)聽key的回收
//Entry類繼承弱引用
private static class Entry<K,V> extends WeakReference<Object> {}
//WeakHashMap中的實例變量用于監(jiān)聽key的回收
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
流程
當WeakHashMap中Key沒有其他強引用時候,發(fā)生了一次任意GC佣盒,由于Entry的Key是弱引用挎袜,那么被GC回收。
被GC回收的同時肥惭,Entry類(繼承了弱引用)會由GC自動放入到Reference的靜態(tài)屬性pending中宋雏。
Reference類在類初始化后有個static代碼塊,里面啟動了一個高優(yōu)先級的daemon線程务豺,用于把pending指向的Reference對象放到ReferenceQueue中
經過3后磨总,Entry就被丟到了ReferenceQueue中
當你在GC后調用WeakHashMap的get、put笼沥、size等方法時候蚪燕,他會調用自己的expungeStaleEntries()方法。
expungeStaleEntries方法會poll出queue中的Entry類奔浅,然后把entry類處理下馆纳,value=null,釋放掉value的強引用汹桦,然后處理前后的指針鲁驶,size--之類。
經過6之后舞骆,key對應的value強引用也被抹去了钥弯,所以GC也可以把value以及整個entry回收掉。