• 技术文章 >Java >java教程

    详解Java Reference源码分析代码

    高洛峰高洛峰2017-03-19 15:37:56原创1001
    @(Java)[Reference]

    Java Reference 源码分析

    Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互。即可以使用Reference对象来引用其它对象,但是最后还是会被垃圾收集器回收。程序有时候也需要在对象回收后被通知,以告知对象的可达性发生变更。
    Java提供了四种不同类型的引用,引用级别从高到低分别为FinalReference,SoftReference,WeakReference,PhantomReference。其中FinalReference不对外提供使用。每种类型对应着不同级别的可达性。

    简介

    强引用FinalReference

    强引用指的是,程序中有直接可达的引用,而不需要通过任何引用对象,如Object obj = new Object();中,obj为强引用。

    软引用SoftReference

    软引用,非强引用,但是可以通过软引用对象来访问。软引用的对象,只有在内存不足的时候(抛出OOM异常前),垃圾收集器会决定回收该软引用所指向的对象。软引用通常用于实现内存敏感的缓存。

    SoftReference<Object> softRef = new SoftReference<Object>(new Object());

    弱引用WeakReference

    弱引用,非强引用和软引用,但是可以通过弱引用对象来访问。弱引用的对象,不管内存是否足够,只要被垃圾收集器发现,该引用的对象就会被回收。实际的应用见WeakHashMap等。

    WeakReference<Object> weakRef = new WeakReference<Object>(new Object());

    虚引用PhantomReference

    虚引用,该引用必须和引用队列(ReferenceQueue)一起使用,一般用于实现追踪垃圾收集器的回收动作,比如在对象被回收的时候,会调用该对象的finalize方法,在使用虚引用可以实现该动作,也更加安全。

    Object obj = new Object();
    ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
    PhantomReference<Object> phantom = new PhantomReference<Object>(obj, refQueue);

    ReferenceQueue

    该队列作为引用中的一员,可以和上述三种引用类型组合使用,该队列的作用是:创建Reference时,将Queue注册到Reference中,当该Reference所引用的对象被垃圾收集器回收时,会将该Reference放到该队列中,相当于一种通知机制。
    示例 Demo1:

    ReferenceQueue queue = new ReferenceQueue();
    WeakReference reference = new WeakReference(new Object(), queue);
    System.out.println(reference);
    System.gc();
    Reference reference1 = queue.remove();
    System.out.println(reference1);

    源码分析

    Reference和ReferenceQueue

    Reference内部有几个比较重要的属性

    // 用于保存对象的引用,GC会根据不同Reference来特别对待
    private T referent;
    // 如果需要通知机制,则保存的对对应的队列
    ReferenceQueue<? super T> queue;
    /* 这个用于实现一个单向循环链表,用以将保存需要由ReferenceHandler处理的引用 */
    Reference next;
    static private class Lock { };
    // 锁,用于同步pending队列的进队和出队
    private static Lock lock = new Lock();
    // 此属性保存一个PENDING的队列,配合上述next一起使用
    private static Reference pending = null;

    状态

    详解Java Reference源码分析代码

    内部类ReferenceHandler

    ReferenceHandler作为Reference的静态内部类,用于实现将pending队列里面的Reference实例依次添加到不同的ReferenceQueue中(取决于Reference里面的queue)。该pending的元素由GC负责加入。
    注:这里对pending队列进行加锁,个人认为是因为GC线程可能和ReferenceHandler所在的线程并发执行,如GC采用CMS并发收集的时候。
    如下代码所示

    // 此线程在静态块中启动,即一旦使用了Reference,则会启动该线程
    private static class ReferenceHandler extends Thread {
        public void run() {
            for (;;) {
    
                Reference r;
                synchronized (lock) {
                    if (pending != null) {
                        r = pending;
                        Reference rn = r.next;
                        // 从pending中取下一个元素,如果后继为空,则next指向自身                    pending = (rn == r) ? null : rn;
                        r.next = r;
                    } else {
                        try {
                            // 没有则等待,后续加入元素会调用lock.notify唤醒
                            lock.wait();
                        } catch (InterruptedException x) { }
                        continue;
                    }
                }
                // ...
                ReferenceQueue q = r.queue;
                // 如果该Reference注册了对应的Queue,则加入到该Queue中
                if (q != ReferenceQueue.NULL) q.enqueue(r);
            }
        }
    }

    ReferenceQueue属性

    // 用于标识没有注册Queue
    static ReferenceQueue NULL = new Null();
    // 用于标识已经处于对应的Queue中
    static ReferenceQueue ENQUEUED = new Null();
    
    static private class Lock { };
    /* 互斥锁,用于同步ReferenceHandler的enqueue和用户线程操作的remove和poll出队操作 */
    private Lock lock = new Lock();
    // 队列
    private volatile Reference<? extends T> head = null;
    // 队列中的元素个数
    private long queueLength = 0;

    ReferenceQueue.enqueue

    只会通过Reference里要调用该方法,用于将Reference放入到当前队列中

    boolean enqueue(Reference<? extends T> r) {    synchronized (r) {        // 判断是否已经入队了
            if (r.queue == ENQUEUED) return false;        synchronized (lock) {
                r.queue = ENQUEUED;            // 单向循环
                r.next = (head == null) ? r : head;
                head = r;
                queueLength++;            if (r instanceof FinalReference) {
                    sun.misc.VM.addFinalRefCount(1);
                }            // 通知当前挂起的线程(调用remove时有可能会挂起)
                lock.notifyAll();            return true;
            }
        }
    }

    ReferenceQueue.remove

    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;        for (;;) {            // 否则等待,由enqueue时notify唤醒
                lock.wait(timeout);
                r = reallyPoll();            if (r != null) return r;            if (timeout != 0) return null;
            }
        }
    }

    具体执行流程

    以上述示例Demo1作为分析

    // 创建一个引用队列
    ReferenceQueue queue = new ReferenceQueue();
    
    // 创建虚引用,此时状态为Active,并且Reference.pending为空,当前Reference.queue = 上面创建的queue,并且next=null
    WeakReference reference = new WeakReference(new Object(), queue);
    System.out.println(reference);
    // 当GC执行后,由于是虚引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDING
    System.gc();
    
    /* ReferenceHandler从pending中取下该元素,并且将该元素放入到queue中,此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED */
    
    /* 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL */
    Reference reference1 = queue.remove();
    System.out.println(reference1);

    以上就是详解Java Reference源码分析代码的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:Java Reference
    上一篇:JMM java内存模型图文详解 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • Java数据结构之单链表与OJ题• 详细介绍Java正则表达式之单字符匹配和预定义字符• Java总结分享之反射、枚举、Lambda表达式• 一起来分析java设计模式之单例• 一文搞懂Java线程池实现原理
    1/1

    PHP中文网