【Java】 DirectByteBuffer堆外內存回收( 二 )

Cleaner從名字上可以看出與清理有關,BitsreserveMemory方法底層也是通過Cleaner來進行清理,所以Cleaner是重點關注的類 。
DeallocatorDirectByteBuffer的一個內部類,并且實現了Runnable接口,在run方法中可以看到對內存進行了釋放,接下來就去看下在哪里觸發Deallocator任務的執行:
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {private static class Deallocator implements Runnable {// ...private Deallocator(long address, long size, int capacity) {assert (address != 0);this.address = address; // 設置內存地址this.size = size;this.capacity = capacity;}public void run() {if (address == 0) {// Paranoiareturn;}// 釋放內存unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);}}}CleanerCleaner繼承了PhantomReference,PhantomReferenceReference的子類 , 所以Cleaner是一個虛引用對象 。

【Java】 DirectByteBuffer堆外內存回收

文章插圖
創建Cleaner虛引用需要與引用隊列結合使用,所以在Cleaner中可以看到有一個ReferenceQueue , 它是一個靜態的變量 , 所以創建的所有Cleaner對象都會共同使用這個引用隊列 。
在創建Cleaner的create方法中 , 處理邏輯如下:
  1. 通過構造函數創建了一個Cleaner對象 , 構造函數中的referent參數為DirectByteBuffer , thunk參數為Deallocator對象,在構造函數中又調用了父類的構造函數完成實例化;
  2. 調用add方法將創建的Cleaner對象加入到鏈表中 , 添加到鏈表的時候使用的是頭插法,新加入的節點放在鏈表的頭部,first成員變量是一個靜態變量,它指向鏈表的頭結點,創建的Cleaner都會加入到這個鏈表中;
創建后的Cleaner對象處于Active狀態 。
public class Cleaner extends PhantomReference<Object>{// ReferenceQueue隊列private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();// 靜態變量,鏈表的頭結點,創建的Cleaner都會加入到這個鏈表中static private Cleaner first = null;// thunkprivate final Runnable thunk;public static Cleaner create(Object ob, Runnable thunk) {if (thunk == null)return null;// 創建一個Cleaner并加入鏈表return add(new Cleaner(ob, thunk));}private Cleaner(Object referent, Runnable thunk) {super(referent, dummyQueue); // 調用父類構造函數 , 傳入引用對象和引用隊列this.thunk = thunk; // thunk指向傳入的Deallocator}private static synchronized Cleaner add(Cleaner cl) {// 如果頭結點不為空if (first != null) {// 將新加入的節點作為頭結點cl.next = first;first.prev = cl;}first = cl;return cl;}}Cleaner調用父類構造函數時,最終會進入到父類Reference中的構造函數中:
referent:指向實際的引用對象,上面創建的是DirectByteBuffer,所以這里指向的是DirectByteBuffer
queue:引用隊列,指向Cleaner中的引用隊列dummyQueue
public class PhantomReference<T> extends Reference<T> {// ...public PhantomReference(T referent, ReferenceQueue<? super T> q) {super(referent, q); // 調用父類構造函數}}public abstract class Reference<T> {/* 引用對象 */private T referent;// 引用隊列volatile ReferenceQueue<? super T> queue;Reference(T referent, ReferenceQueue<? super T> queue) {this.referent = referent;// 設置引用隊列this.queue = (queue == null) ? ReferenceQueue.NULL : queue;}}
【Java】 DirectByteBuffer堆外內存回收

文章插圖
啟動ReferenceHandler線程Reference中有一個靜態方法 , 里面創建了一個ReferenceHandler并設置為守護線程,然后啟動了該線程 , 并創建了JavaLangRefAccess對象設置到SharedSecrets中:
public abstract class Reference<T> {static {ThreadGroup tg = Thread.currentThread().getThreadGroup();for (ThreadGroup tgn = tg;tgn != null;tg = tgn, tgn = tg.getParent());// 創建ReferenceHandlerThread handler = new ReferenceHandler(tg, "Reference Handler");// 設置優先級為最高handler.setPriority(Thread.MAX_PRIORITY);handler.setDaemon(true);handler.start();// 這里設置了JavaLangRefAccessSharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {@Overridepublic boolean tryHandlePendingReference() {// 調用了tryHandlePendingreturn tryHandlePending(false);}});}}

推薦閱讀