java源代码 Reference和ReferenceQueue分析

这是我个人对源码的理解,也希望大家批评指正。Reference主要是负责内存的一个状态,当然它还和java虚拟机,垃圾回收器打交道。Reference类首先把内存分为4种状态Active,Pending,Enqueued,Inactive,

1.一般来说内存一开始被分配的状态都是Active,

2.只有注册了队列的对象(构造的时候传了队列对象参数,即ReferenceQueue<? super T> queue;)才会处于这个状态,Pending大概是指快要被放进队列的对象,进入3。

3.只有注册了队列的对象才会处于这个状态,Enqueued就是对象的内存将要被回收,目前还没有回收我们已经把这个对象放入到一个队列中,方便我们查询某个对象是否将要被回收,如果某个对象在队列里,说明这个对象快要被垃圾回收器回收

4.Inactive就是最终的状态,不能再变为其它状态。说明对象已经不存在了

在内存分配的时候我么可以选择给它传入一个队列参数,这个参数的类名叫作ReferenceQueue,当然也可以选择不传入,取决于构造函数。

Reference类源码

 /* -- Constructors -- */


    Reference(T referent) {
        this(referent, null);
    }


    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

Reference类里面有两个值得注意的成员变量

 /* When active:   next element in a discovered reference list maintained by GC (or this if last)
     *     pending:   next element in the pending list (or null if last)
     *   otherwise:   NULL
     */

    transient private Reference<T> discovered;  /* used by VM */

从英文注释大致的意思是说discovered表示下一个要回收的对象,请注意,目前他不是正要回收的对象,正要回收的对象是后面要提到的pending 变量,并且这个是由垃圾回收器控制。



    /* 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;

pending这个变量表示它是下一个要进入ReferenceQueue队列的对象,并且它也是目前正要回收的对象。

我们从Reference源码中发现没有给discovered和pending 赋值的地方,也就是说pending和discovered很有可能是垃圾回收器给它们赋值。

这个队列ReferenceQueue到底有什么作用呢

查看ReferenceQueue的源码,发现这个类里面主要的的方法也就是2个

enqueue这个方法其实比较简单,首先是用lock这个锁实现同步,也就是在某个时间段只能有一个线程访问,后面的也就是把对象插入进去,新插进的元素在最上面,也就是头部。

ReferenceQueue类的源码

进队

  boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
            // Check that since getting the lock this reference hasn't already been
            // enqueued (and even then removed)
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;
            r.queue = ENQUEUED;
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

reallyPoll这个函数主要是从队列里面取出元素,表面上说是队列,个人感觉它就像栈,

出队

   private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        if (r != null) {
            head = (r.next == r) ?
                null :
                r.next; // Unchecked due to the next field having a raw type in Reference
            r.queue = NULL;
            r.next = r;
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }




ReferenceQueue这个类有什么用呢,它跟Reference有什么关系呢,关系主要体现在这几个方面,首先Reference这个类里面在构造函数的时候有两种选择,一种是给它传入一个ReferenceQueue,一种是不传,如果不传的话,等这个对象的内存被回收了,直接从Active变为Inactive状态,如果我们传入了ReferenceQueue,那么当对象的内存回收的时候会经历一个过程,从Active->Pending->Enqueued->Inactive。pending状态就是等待着进入ReferenceQueue队列的这样一个状态,说白了它目前还没被回收,只是对象的引用(用户代码中的引用)被移除了,pending保存了这个引用,回收的过程中,ReferenceHandler这个线程会把该对象的引用(pending)放入到我们在构造函数时传入的那个队列里面,总结一下,当经历第三个状态的时候,和ReferenceQueue的关系很密切,

Reference类里面的源码,这部分源码的意思大概是开启一个后台线程,并且这个线程的优先级很高,一直负责循环回收内存,只要有内存可以回收,就马上回收,没有的话就 阻塞等待,但是值得注意的是ReferenceQueue<Object> q = r.queue;if (q != ReferenceQueue.NULL) q.enqueue(r); 这段代码,也就是说如果被回收对象里面的成员queue不为空,当它被回收时,我们就把它放入到队列里面,进入到前面提到的第三个状态,这个过程能发生,要有个条件,就是前面提到的,你必须在构造该对象时传一个队列参数给这个对象得成员queue。我们把这个对象放到队列里有什么用,当我们在用软引用的时候,可以去查询这个队列,来知道对象内存是不是快要被回收了。

Reference类源码   ReferenceHandler 是Reference的一个内部类

  private static class ReferenceHandler extends Thread {


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


        public void run() {
            for (;;) {
                Reference<Object> r;
                synchronized (lock) {
                    if (pending != null) {
                        r = pending;
                        pending = r.discovered;
                        r.discovered = null;
                    } else {
                   
                        try {
                            try {
                                lock.wait();
                            } catch (OutOfMemoryError x) { }
                        } catch (InterruptedException x) { }
                        continue;
                    }
                }


                // Fast path for cleaners
                if (r instanceof Cleaner) {
                    ((Cleaner)r).clean();
                    continue;
                }


                ReferenceQueue<Object> q = r.queue;
                if (q != ReferenceQueue.NULL) q.enqueue(r);
            }
        }
    }


    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();
    }

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用ReferenceQueue的示例代码: ``` import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class ReferenceQueueDemo { public static void main(String[] args) { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue); // 检查弱引用是否被加入到了ReferenceQueue中 new Thread(() -> { while (true) { Reference<?> ref = referenceQueue.poll(); if (ref != null) { System.out.println("Weak reference is garbage collected: " + ref); } } }).start(); // 立即清除强引用 obj = null; System.gc(); // 等待一段时间以便垃圾回收完成 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 在这个示例中,我们创建了一个Object对象,并将其封装在一个WeakReference中。我们同时创建了一个ReferenceQueue对象,用于存放被垃圾回收的引用对象。在一个单独的线程中,我们不断地检查ReferenceQueue中是否有弱引用对象被回收了。 接着,我们立即将强引用obj设为null,并手动调用System.gc()方法,以便触发垃圾回收。最后,我们等待一段时间以便垃圾回收完成。 在这个示例中,我们通过使用ReferenceQueue来监控弱引用对象是否被回收,从而及时进行必要的清理工作。如果垃圾回收器回收了弱引用对象,那么该对象将会被加入到ReferenceQueue中,并且我们可以在另一个线程中对其进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值