【Java】 DirectByteBuffer堆外内存回收( 三 )

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);}});}}ReferenceHandlerReference的内部类,继承了Thread,在run方法中开启了一个循环,不断的执行tryHandlePending方法,处理Reference中pending列表:
public abstract class Reference<T> {private static class ReferenceHandler extends Thread {// ...ReferenceHandler(ThreadGroup g, String name) {super(g, name);}public void run() {while (true) {// 处理pending列表tryHandlePending(true);}}} }Cleaner会启动一个优先级最高的守护线程,不断调用tryHandlePending来检测是否有需要回收的引用对象(还未进行真正的回收),然后进行处理 。
处理pending列表垃圾回收器会将要回收的引用对象放在Referencepending变量中,从数据类型上可以看出pending只是一个Reference类型的对象,并不是一个list,如果有多个需要回收的对象,如何将它们全部放入pending对象中?
可以把pengding看做是一个链表的头结点,假如有引用对象被判定需要回收,如果pengding为空直接放入即可,如果不为空,将使用头插法将新的对象加入到链表中,也就是将新对象的discovered指向pending对象,然后将pending指向当前要回收的这个对象,这样就形成了一个链表,pending指向链表的头结点 。
【Java】 DirectByteBuffer堆外内存回收

文章插图
在pending链表中的引用对象处于pending状态 。
接下来看tryHandlePending方法的处理逻辑:
  1. 如果pending不为空,表示有需要回收的对象,此时将pengding指向的对象放在临时变量r中,并判断是否是Cleaner类型,如果是将其强制转为Cleaner,记录在临时变量c中,接着更新pending的值为r的discovered,因为discovered中记录了下一个需要被回收的对象,pengding需要指向下一个需要被回收的对象;

    经验总结扩展阅读