Java引用类型之:Reference源码解析

2019-07-14  本文已影响0人  贪睡的企鹅

1 简介

Reference是所有引用类型的父类,定义了引用的公共行为和操作,

2 Reference类结构

image

Reference类与垃圾回收是密切配合的,所以该类不能被直接子类化。简单来讲,Reference的继承类都是经过严格设计的,甚至连成员变量的先后顺序都不能改变,所以在代码中直接继承Reference类是没有任何意义的。但是可以继承Reference类的子类。

例如:Finalizer 继承自 FinalReference,Cleaner 继承自 PhantomReference

3 核心属性

3.1 referent引用

referent对象内部存在引用(referent)用来保存引用对象的地址。

public abstract class Reference<T>{
//...省略代码
/**
 * referent引用
 */
private T referent;  
//...省略代码
}

3.2 pending队列

reference对象内部维护着一个pending单项链表队列,当referenc引用对象被判断可以回收时JVM会将reference对象放入此队列。这里需要注意pending是静态变量,意味着是所有Reference对象共享的。所有referenc对象都会放入同一个队列中。

public abstract class Reference<T>{
//...省略代码
    /**
     * pending单项链表队列(就pending表示head指针)
     */
    private static Reference<Object> pending = null;

    /**
     * discovered作为pending队列中每一个Reference指向下一个的引用
     */
    transient private Reference<T> discovered;
}
image
3.3 referenceQueue队列

reference对象内部维护着一个单项链表队列,当referenc引用对象被判断可以回收时,reference对象内部ReferenceHandler线程会将 reference对象从pending队列取出放入referenceQueue这个队列中。通过监控这个队列,取出这个reference对象,就可以对reference对象进行一些善后处理。

实现referenceQueue上是一个单项链表结构,其head指针在ReferenceQueue内部,reference对象内部存在一个属性next将链表节点串联起来

public abstract class Reference<T>{
//...省略代码
    /**
     * referenceQueue单项链表队列(head指针在ReferenceQueue内部)
     */
    volatile ReferenceQueue<? super T> queue;

    /**
     * next作为queue队列中每一个Reference指向下一个的引用
     */
    @SuppressWarnings("rawtypes")
    Reference next;
//...省略代码
}

public class ReferenceQueue<T> {

    /** 标识Reference对象状态(从ReferenceQueue移除) **/
    static ReferenceQueue<Object> NULL = new Null<>();
    
    /** 标识Reference对象状态(加入ReferenceQueue队列) **/
    static ReferenceQueue<Object> ENQUEUED = new Null<>();

    /**
     * 链表头部引用
     */
    private volatile Reference<? extends T> head = null;

    /**
     * 链表节点长度
     */
    private long queueLength = 0;
    //...省略代码
}    
image

入队操作

**
     * 这个方法仅会被Reference中ReferenceHandler线程调用
     * 将Reference加入ReferenceQueue队列中
     */
    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
            /**
             * 如果Reference对象没有设置queue,或者已经加入ReferenceQueue队列中 (queue == ENQUEUED)
             * 直接返回false
             * **/
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }

            assert queue == this;

            /** 标识Reference对象加入ReferenceQueue队列   **/
            r.queue = ENQUEUED;
            /** 将Reference加入ReferenceQueue队列中 **/
            r.next = (head == null) ? r : head;
            head = r;
            /** 队列数量+1 **/
            queueLength++;
            /** 如果Reference对象为FinalReference 引用数量+1 **/
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

出队操作

/**
     * 从队列头部弹出节点
     */
    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }

    @SuppressWarnings("unchecked")
    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        /** 将Reference从ReferenceQueue队列中取出 **/
        if (r != null) {
            /** 获取ReferenceQueue队列head之后Reference对象 **/
            head = (r.next == r) ?
                null :
                r.next;

            /** 标识Reference对象从ReferenceQueue队列中被取出  **/
            r.queue = NULL;
            r.next = r;

            /** 队列数量+1 **/
            queueLength--;

            /** 如果Reference对象为FinalReference 引用数量+1 **/
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            /** 返回 **/
            return r;
        }
        return null;
    }

删除操作

 /**
     * 删除队列元素
     */
    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);
    }


    /**
     * 移除并返回队列首节点,此方法将阻塞到获取到一个Reference对象或者超时才会返回
     *  timeout时间的单位是毫秒
     */
    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;
                }
            }
        }
    }

应用场景

ReferenceQueue一般用来与SoftReference、WeakReference或者PhantomReference配合使用,将需要关注的引用对象注册到引用队列后,便可以通过监控该队列来判断关注的对象是否被回收,从而执行相应的方法。

1 使用引用队列进行数据监控什么时候回收

当我们使用有限的堆空间,不断创建大的对象,并将大对象被弱引用唯一指向,由于是弱可达对象在内存不足时gc都会清理,我们通过一个线程不断监听ReferenceQueue中数据,感知哪些对象被回收

//-verbose:gc -Xms4m -Xmx4m -Xmn2m
public class ReferenceQueueTest {

    private static ReferenceQueue<byte[]> rq = new ReferenceQueue<>();
    private static int _1M = 1024 * 1024;

    public static void main(String[] args) {
        Object value = new Object();
        Map<WeakReference<byte[]>, Object> map = new HashMap<>();
        Thread thread = new Thread(ReferenceQueueTest::run);
        thread.setDaemon(true);
        thread.start();

        for(int i = 0;i < 100;i++) {
            byte[] bytes = new byte[_1M];
            WeakReference<byte[]> weakReference = new WeakReference<>(bytes, rq);
            map.put(weakReference, value);
            bytes=null;
        }
        System.out.println("map.size->" + map.size());

        int aliveNum = 0;
        for (Map.Entry<WeakReference<byte[]>, Object> entry : map.entrySet()){
            if (entry != null){
                if (entry.getKey().get() != null){
                    aliveNum++;
                }
            }
        }
        System.out.println("total" + aliveNum);
    }

    private static void run() {
        try {
            int n = 0;
            WeakReference k;
            while ((k = (WeakReference) rq.remove()) != null) {
                System.out.println((++n) + "clear:" + k);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2 队列监控的反向操作

反向操作,即意味着一个数据变化了,可以通过Reference对象反向拿到相关的数据,从而进行后续的处理。下面有个小栗子:

public class ReferenceQueueTest2 {

    private static ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
    private static int _1M = 1024 * 1024;

    public static void main(String[] args) throws InterruptedException {
        final Map<Object, MyWeakReference> hashMap = new HashMap<>();
        Thread thread = new Thread(() -> {
            try {
                int n = 0;
                MyWeakReference k;
                while(null != (k = (MyWeakReference) referenceQueue.remove())) {
                    System.out.println((++n) + "回收了:" + k);
                    //反向获取,移除对应的entry
                    hashMap.remove(k.key);
                    //额外对key对象作其它处理,比如关闭流,通知操作等
                }
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setDaemon(true);
        thread.start();

        for(int i = 0;i < 10000;i++) {
            byte[] bytesKey = new byte[_1M];
            byte[] bytesValue = new byte[_1M];
            hashMap.put(bytesKey, new MyWeakReference(bytesKey, bytesValue, referenceQueue));
        }
    }

    static class MyWeakReference extends WeakReference<byte[]> {
        private Object key;
        MyWeakReference(Object key, byte[] referent, ReferenceQueue<? super byte[]> q) {
            super(referent, q);
            this.key = key;
        }
    }
}

如果没有这个队列,就只能通过不断地轮询reference对象,通过get方法是否返回null( phantomReference对象不能这样做,其get方法始终返回null,因此它只有带queue的构造函数 )来判断对象是否被回收。

4 reference生命周期

reference引用对象一共有四种状态,Active(活跃状态)、Pending(半死不活状态)、Enqueued(濒死状态)、Inactive(凉凉状态),

reference引用对象状态和reference对象状态相互关联,我们可以通过感知reference对象来判断reference指针对象正处于何种状态

reference引用状态图

image

reference对象状态图

Reference对象生命周期.jpg

5 ReferenceHandler线程

ReferenceHandler类是Reference类的一个静态内部类,继承自Thread,所以这条线程就叫它ReferenceHandler线程。
用来不断轮询判断当前对象是否加入Pending队列,如果已经加入则将Reference对象从Pending队列出队,入队ReferenceQueue队列。并可以针对Reference子类实现做扩展

初始化启动

Reference内部通过静态代码块初始化并启动ReferenceHandler线程

static {
        /** 获取当前线程最高线程父类的分组 **/
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());

        /** 实例ReferenceHandler并启动ReferenceHandler线程 **/
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

ReferenceHandler结构

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 {
            /** 初始化InterruptedException **/
            ensureClassInitialized(InterruptedException.class);
            /** 初始化Cleaner **/
            ensureClassInitialized(Cleaner.class);
        }

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

        /** run **/
        public void run() {
             /**  死循环调用  **/
            while (true) {
                tryHandlePending(true);
            }
        }
    }

ReferenceHandler执行逻辑

判断是当前对象是否加入pending,且是队列首节点,如果是加入ReferenceQueue队列,同时如果Reference实现为Cleaner,调用clean方法做清理工作

static boolean tryHandlePending(boolean waitForNotify) {
            Reference<Object> r;
            Cleaner c;
            try {
                synchronized (lock) {
                    /** 判断是当前对象是否加入pending,且是队列首节点 **/
                    if (pending != null) {
                        /** 当前对象从pending队列出队 **/
                        r = pending;
                        pending = r.discovered;
                        r.discovered = null;
                        /** 判断当前对象是否是Cleaner  **/
                        c = r instanceof Cleaner ? (Cleaner) r : null;
                    } else {
                        /** 等待 **/
                        if (waitForNotify) {
                            lock.wait();
                        }
                        // retry if waited
                        return waitForNotify;
                    }
                }
            } catch (OutOfMemoryError x) {
                Thread.yield();
                return true;
            } catch (InterruptedException x) {
                return true;
            }

            /** Cleaner清理工作 **/
            if (c != null) {
                c.clean();
                return true;
            }

            /** 加入ReferenceQueue队列 **/
            ReferenceQueue<? super Object> q = r.queue;
            if (q != ReferenceQueue.NULL) q.enqueue(r);
            return true;
        }

6 其他方法

  /**
     * referent引用
     */
    public T get() {
        return this.referent;
    }

    /**
     * 清理referent引用
     */
    public void clear() {
        this.referent = null;
    }


    /**
     * referent引用对象是否为Enqueued状态,referent对象添加到ReferenceQueue队列
     */
    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);
    }

    /**
     * 将referent对象添加到ReferenceQueue队列
     */
    public boolean enqueue() {
        return this.queue.enqueue(this);
    }
上一篇下一篇

猜你喜欢

热点阅读