Java Reference--2

Reference生命周期


当Reference引用的对象不可达时,Reference对象自身会被放入一个pending队列。同时有一个引用处理线程会不断从队列里面拉取Reference对象进行处理。

Reference成员变量

// Reference所引用的对象
private T referent;         /* Treated specially by GC */

// 保存Reference对象的队列,如果有注册队列则回收引用会加入该队列
volatile ReferenceQueue<? super T> queue;

//上面引用队列referenceQueue中保存引用的链表
/* When active:   NULL
 *     pending:   this
 *    Enqueued:   next reference in queue (or this if last)
 *    Inactive:   this
 */
@SuppressWarnings("rawtypes")
Reference next;

/* When active:   next element in a discovered reference list maintained by GC (or this if last) 由gc管理的引用发现链表的下一个引用
 *     pending:   next element in the pending list (or null if last)
 *   otherwise:   NULL
 */
transient private Reference<T> discovered;  /* used by VM */


//等待入队列的引用链表,gc往该链表加引用对象,Reference-handler线程消费该链表,它通过discovered连接它的元素
/* List of References waiting to be enqueued.  The collector adds
 * References to this list, while the Reference-handler thread removes
 * them.  This list is protected by the above lock object. The
 * list uses the discovered field to link its elements.
 */
private static Reference<Object> pending = null;


//最高优先级的守护线程
private static class ReferenceHandler

Reference有4中状态

/* A Reference instance is in one of four possible internal states:
 *
 *     Active: Subject to special treatment by the garbage collector.  Some
 *     time after the collector detects that the reachability of the
 *     referent has changed to the appropriate state, it changes the
 *     instance's state to either Pending or Inactive, depending upon
 *     whether or not the instance was registered with a queue when it was
 *     created.  In the former case it also adds the instance to the
 *     pending-Reference list.  Newly-created instances are Active.
 *
 *     Pending: An element of the pending-Reference list, waiting to be
 *     enqueued by the Reference-handler thread.  Unregistered instances
 *     are never in this state.
 *
 *     Enqueued: An element of the queue with which the instance was
 *     registered when it was created.  When an instance is removed from
 *     its ReferenceQueue, it is made Inactive.  Unregistered instances are
 *     never in this state.
 *
 *     Inactive: Nothing more to do.  Once an instance becomes Inactive its
 *     state will never change again.
 *
 * The state is encoded in the queue and next fields as follows:
 *
 *     Active: queue = ReferenceQueue with which instance is registered, or
 *     ReferenceQueue.NULL if it was not registered with a queue; next =
 *     null.
 *
 *     Pending: queue = ReferenceQueue with which instance is registered;
 *     next = this
 *
 *     Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
 *     in queue, or this if at end of list.
 *
 *     Inactive: queue = ReferenceQueue.NULL; next = this.
 *

active: 活动状态,对象存在强引用状态,还没有被回收

pending: 垃圾回收器将没有强引用的Reference对象放入到pending队列中,等待ReferenceHander线程处理(前提是这个Reference对象创建的时候传入了ReferenceQueue,否则的话对象会直接进入Inactive状态)

Enqueued:ReferenceHander线程将pending队列中的对象取出来放到ReferenceQueue队列里;

Inactive:处于此状态的Reference对象可以被回收,并且其内部封装的对象也可以被回收掉了

状态之间的转换如图

Reference对象的状态主要由queue和next这两个成员决定

active: queue = ReferenceQueue, next = null;
pending: queue = ReferenceQueue, next = this;
enqueued: queue = ReferenceQueue.ENQUEUED
inactive: queue = ReferenceQueue.NULL, next = this.

ReferenceQueue

ReferenceQueue是一个链表,链表里的元素是加入进去的Reference实例,使用的是链表的头插法,所以是一个先进后出的队列。

ReferenceQueue是使用wati()和notifyAll()实现生产者和消费者

ReferenceQueue源码解析

public ReferenceQueue() { }

//内部类,用于标识队列状态
private static class Null<S> extends ReferenceQueue<S> {
    boolean enqueue(Reference<? extends S> r) {
        return false;
    }
}

//标识该引用已被当前队列移除过,当Reference对象创建时没有指定queue或Reference对象已经处于inactive状态
static ReferenceQueue<Object> NULL = new Null<>(); 

//标识该引用已加入当前队列, 当Reference已经被ReferenceHander线程从pending队列移到queue里面时
static ReferenceQueue<Object> ENQUEUED = new Null<>();


// 入队操作
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
    synchronized (lock) {
       // 已经移除过,或者已经入队的直接返回
        ReferenceQueue<?> queue = r.queue;
        if ((queue == NULL) || (queue == ENQUEUED)) {
            return false;
        }

        //只有r的队列是当前队列才允许入队
        assert queue == this;

        queue设置为ENQUEUED状态,标识Reference已经入队
        r.queue = ENQUEUED;

        // 头插法,插入队列
        r.next = (head == null) ? r : head;
        head = r;

        //队列长度加1
        queueLength++;
        if (r instanceof FinalReference) {
            sun.misc.VM.addFinalRefCount(1);
        }

        //通知消费者可以消费了
        lock.notifyAll();
        return true;
    }
}

// 出队操作,将头部第一个对象移出队列并返回,如果队列为空,则等待timeout时间后,返回null,这个方法会阻塞线程
public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
{
    if (timeout < 0) {
        throw new IllegalArgumentException("Negative timeout value");
    }
    synchronized (lock) {
         // 若队列不为空,则直接返回队头
        Reference<? extends T> r = reallyPoll();
        if (r != null) return r;
        long start = (timeout == 0) ? 0 : System.nanoTime();
        for (;;) {
             // 队列为空,则等待对象入队
            lock.wait(timeout);
            r = reallyPoll();
            if (r != null) return r;
            if (timeout != 0) {
                long end = System.nanoTime();
                timeout -= (end - start) / 1000_000;
                if (timeout <= 0) return null;
                start = end;
            }
        }
    }
}

// 将头部第一个对象移出队列并返回
private Reference<? extends T> reallyPoll() {       /* Must hold lock */
    Reference<? extends T> r = head;
    if (r != null) {
        head = (r.next == r) ? null :r.next;             
        r.queue = NULL;
        r.next = r;
        queueLength--;
        if (r instanceof FinalReference) {
            sun.misc.VM.addFinalRefCount(-1);
        }
        return r;
    }
    return null;
}

ReferenceHandler

ReferenceHandler线程是一个拥有最高优先级的守护线程。它的任务就是当pending队列不为空的时候,循环将pending队列里面的头部的Reference移除出来,如果这个对象是个Cleaner实例,那么就直接执行它的clean方法来执行清理工作;否则放入到它自己的ReferenceQueue里面

/* High-priority thread to enqueue pending References
 */
private static class ReferenceHandler extends Thread {

    private static void ensureClassInitialized(Class<?> clazz) {
        try {
            Class.forName(clazz.getName(), true, clazz.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
        }
    }

    static {
        // pre-load and initialize InterruptedException and Cleaner classes
        // so that we don't get into trouble later in the run loop if there's
        // memory shortage while loading/initializing them lazily.
        ensureClassInitialized(InterruptedException.class);
        ensureClassInitialized(Cleaner.class);
    }

    ReferenceHandler(ThreadGroup g, String name) {
        super(g, name);
    }

    public void run() {
        while (true) {
            //循环处理pending队列
            tryHandlePending(true);
        }
    }
}

static boolean tryHandlePending(boolean waitForNotify) {
    Reference<Object> r;
    Cleaner c;
    try {
        synchronized (lock) {
        //若队列不为空,则取队列的第一个对象
            if (pending != null) {
                r = pending;
                c = r instanceof Cleaner ? (Cleaner) r : null;
                // 指向队列的下一个节点,这样队头就出列了
                pending = r.discovered;
                r.discovered = null;
            } else {
                // 队列为空,则等待唤醒
                if (waitForNotify) {
                    lock.wait();
                }
                // retry if waited
                return waitForNotify;
            }
        }
    } catch (OutOfMemoryError x) {
         return true;
    } catch (InterruptedException x) {
        // retry
        return true;
    }

    // 如果是Cleaner对象,则执行其clean方法
    if (c != null) {
        c.clean();
        return true;
    }

    // 将引用对象放入ReferenceQueue
    ReferenceQueue<? super Object> q = r.queue;
    if (q != ReferenceQueue.NULL) q.enqueue(r);
    return true;
}

在 tryHandlePending()方法里面,检查 pending是否为null,如果pending不为null,则将pending进行enqueue,否则线程进入 wait状态。简单来说,垃圾回收器会把 References添加进入,ReferenceHandler会移除它,即discovered和pending是由垃圾回收器进行赋值的

总结

Reference具有4中状态,当Reference与ReferenceQueue一起使用时,若对象被回收,那么Reference就会被放入到ReferenceQueue中。这个过程由最高优先级的ReferenceHandler线程来处理。

参考文献


Reprint please specify: wbl Java Reference--2

Previous
Java8 HashMap Java8 HashMap
HashMap数据结构数组 + 链表HashMap要实现哈希表的效果,尽量实现O(1)级别的增删改查,它的具体实现是同时使用了数组和链表。 首先HashMap的内部定义了静态类Node来实现Entry接口 static class Nod
2020-03-07
Next
Java Reference--1 Java Reference--1
GC与Reference在JVM进行GC的时候,JVM首先需要判断一个对象是否可以被回收。JVM从GC ROOT的对象开始向下搜索,搜索所走过的路径称为引用链(reference chain),当一个对象没有任何引用时,就认为这个对象不可达
2020-02-22