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


pending如果为NULL,会进入到else的处理逻辑,返回值为参数传入的waitForNotify的值 。
  • 判断Cleaner对象是否为空,如果不为空,调用Cleaner的clean方法进行清理;
  • 获取引用对象关联的引用队列,然后调用enqueue方法将引用对象加入到引用队列中;
  • 返回true;
  • public abstract class Reference<T> {// 指向pending列表中的下一个节点transient private Reference<T> discovered;// 静态变量pending列表,可以看做是一个链表,pending指向链表的头结点private static Reference<Object> pending = null;static boolean tryHandlePending(boolean waitForNotify) {Reference<Object> r;Cleaner c;try {synchronized (lock) {// 如果pending不为空if (pending != null) {// 获取pending执行的对象r = pending;// 如果是Cleaner类型c = r instanceof Cleaner ? (Cleaner) r : null;// 将pending指向下一个节点pending = r.discovered;// 将discovered置为空r.discovered = null;} else {// 等待if (waitForNotify) {lock.wait();}return waitForNotify;}}} catch (OutOfMemoryError x) {Thread.yield();// retryreturn true;} catch (InterruptedException x) {// retryreturn true;}if (c != null) {// 调用clean方法进行清理c.clean();return true;}// 获取引用队列ReferenceQueue<? super Object> q = r.queue;// 如果队列不为空,将对象加入到引用队列中if (q != ReferenceQueue.NULL) q.enqueue(r);// 返回truereturn true;}}释放内存在Cleaner的clean方法中,可以看到,调用了thunk的run方法,前面内容可知,thunk指向的是Deallocator对象,所以会执行Deallocator的run方法,Deallocator的run方法前面也已经看过,里面会对DirectByteBuffer的堆外内存进行释放:
    public class Cleaner extends PhantomReference<Object> {public void clean() {if (!remove(this))return;try {// 调用run方法thunk.run();} catch (final Throwable x) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {if (System.err != null)new Error("Cleaner terminated abnormally", x).printStackTrace();System.exit(1);return null;}});}}}总结
    Cleaner是一个虚引用,它实际引用的对象DirectByteBuffer如果被GC判定为需要回收,会将引用该对象的Cleaner加入到pending列表,ReferenceHandler线程会不断检测pending是否为空,如果不为空,就对其进行处理:
    1. 如果对象类型为Cleaner,就调用Cleaner的clean方法进行清理,Cleaner的clean方法又会调用Deallocator的run方法,里面调用了freeMemory方法对DirectByteBuffer分配的堆外内存进行释放;
    2. 将Cleaner对象加入到与其关联的引用队列中;
    引用队列ReferenceQueue名字听起来是一个队列,实际使用了一个链表,使用头插法将加入的节点串起来,ReferenceQueue中的head变量指向链表的头节点,每个节点是一个Reference类型的对象:
    public class ReferenceQueue<T> {// head为链表头节点private volatile Reference<? extends T> head = null;}Reference中除了discovered变量之外,还有一个next变量,discovered指向的是处于pending状态时pending列表中的下一个元素,next变量指向的是处于Enqueued状态时,引用队列中的下一个元素:
    public abstract class Reference<T> {/* When active:处于active状态时为NULL*pending:this*Enqueued:Enqueued状态时,指向引用队列中的下一个元素*Inactive:this*/@SuppressWarnings("rawtypes")Reference next;/* When active:active状态时,指向GC维护的一个discovered链表中的下一个元素*pending:pending状态时,指向pending列表中的下一个元素*otherwise:其他情况为NULL*/transient private Reference<T> discovered;/* used by VM */}
    【Java】 DirectByteBuffer堆外内存回收

    经验总结扩展阅读