【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继承了PhantomReferencePhantomReferenceReference的子类 , 所以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);}});}}

推荐阅读