WeakReference Reference ReferenceQueue

Posted yaowen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WeakReference Reference ReferenceQueue相关的知识,希望对你有一定的参考价值。

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

技术图片

public class WeakReference<T> extends Reference<T> { 
    public WeakReference(T referent) {
        super(referent);
    } 
    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    } 
}
public abstract class Reference<T> {
    /*
     referent:表示其引用的对象,即我们在构造的时候需要被包装在其中的对象。
      对象即将被回收的定义:此对象除了被reference引用之外没有其它引用了( 并非确实没有被引用,而是gcRoot可达性不可达,以避免循环引用的问题 )。
       如果一旦被回收,则会直接置为null,而外部程序可通过引用对象本身( 而不是referent,这里是reference#get() )
     了解到回收行为的产生( PhntomReference除外 )。
     */
    private T referent;        //其引用的对象
    volatile ReferenceQueue<? super T> queue;//当对象即将被回收时,整个reference对象,会被放到queue 里面,外部程序即可通过监控这个 queue 即可拿到相应的数据了。
    volatile Reference next;//当前引用节点所存储的下一个即将被处理的节点。
    //discovered进行排队,这边只需要不停地拿到pending,然后再通过discovered 不断地拿到下一个对象赋值给pending即可,直到取到了最有一个。它是被JVM 使用的。/
    
    transient private Reference<T> discovered;  /* used by VM */
    private static Reference<Object> pending = null;
    static private class Lock { }
    private static Lock lock = new Lock();//是这个自定义的lock
    //一个线程,run里面是死循环
    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,Cleaner, 
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

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

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }
    //即 discovered和 pending 是由垃圾回收器进行赋值的。
    /*
      可以理解为jvm在gc时会将要处理的对象放到这个静态字段上面。同时,另一个字段discovered:表示要处理的对象的下一个对象。
       即可以理解要处理的对象也是一个链表,通过discovered进行排队,这边只需要不停地拿到pending,
      然后再通过discovered不断地拿到下一个对象赋值给pending即可,
       直到取到了最有一个。因为这个pending对象,两个线程都可能访问,因此需要加锁处理。
     */
    /*
     当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。
     并且这里enqueue的队列是我们在初始化( 构造函数 )Reference对象时传进来的queue, 如果传入了null(
      实际使用的是ReferenceQueue.NULL ),
     则ReferenceHandler则不进行enqueue操作,所以只有非RefernceQueue.
      NULL的queue才会将Reference进行enqueue。
     */
    //ReferenceQueue是作为 JVM GC与上层Reference对象管理之间的一个消息传递方式,它使得我们可以对所监听的对象引用可达发生变化时做一些处理
    static boolean tryHandlePending(boolean waitForNotify) {//垃圾回收器回收之后,把Reference对象入队。
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {//从pending开始,一个个先后找。pending不为 null,则将 pending 进行 enqueue,
                    r = pending;
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    pending = r.discovered;
                    r.discovered = null;
                } else {// 为 null。等待
                    if (waitForNotify) {
                        lock.wait();
                    }
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            Thread.yield();
            return true;
        } catch (InterruptedException x) {
            return true;
        }
        if (c != null) {
            c.clean();
            return true;
        }
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);  //r入队queue
        return true;
    }

    static {//静态代码块,线程开始执行。当 Refrence 类被加载的时候,会执行静态代码块。在静态代码块里面,会启动 ReferenceHandler 线程,
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        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);
            }
        });
    }

    public T get() {
        return this.referent;
    }

    public void clear() {
        this.referent = null;
    }

    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);//是否已经入队
    }

    public boolean enqueue() {//入队
        return this.queue.enqueue(this);
    }

    Reference(T referent) {
        this(referent, null);
    }
    //其中queue的意义在于,我们可以在外部对这个queue进行监控。即如果有对象即将被回收,那么相应的reference对象就会被放到这个queue里。
    //引用对象指向的对象 GC 会自动清理,但是引用对象本身也是对象(是对象就占用一定资源),所以需要我们自己清理
    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;//空队列或者传进来的队列
    }
}
public class ReferenceQueue<T> {
    public ReferenceQueue() { }

    private static class Null<S> extends ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }
    //标记
    static ReferenceQueue<Object> NULL = new Null<>();
    static ReferenceQueue<Object> ENQUEUED = new Null<>();

    static private class Lock { };
    private Lock lock = new Lock();
    private volatile Reference<? extends T> head = null;//头结点是第一个元素开始时候为null进入一个之后,这个进来的就是头结点。后面再添加。
    private long queueLength = 0;

    boolean enqueue(Reference<? extends T> r) { 
        synchronized (lock) {
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {//没有传队列,或者已经入队了,什么都不做。
                return false;
            }
            assert queue == this;
            //在放到队列当中后,其queue就不会再引用这个队列了。而是引用一个特殊的ENQUEUED。因为已经放到队列当中,并且不会再次放到队列当中。
            r.queue = ENQUEUED;//标记为已经入队
            r.next = (head == null) ? r : head;//第一个元素的下一个节点是自己,不是第一个元素,下一个节点是头结点
            head = r;//修改头结点
            queueLength++;//长度
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();//通知外部程序之前阻塞在当前队列之上的情况。( 即之前一直没有拿到待处理的对象,如ReferenceQueue的remove()方法
            return true;
        }
    }

    private Reference<? extends T> reallyPoll() {       
        Reference<? extends T> r = head;//头结点
        if (r != null) {//头结点不为空
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;//
            head = (rn == r) ? null : rn;//下一个节点等于自己,就是最后一个节点了,移除之后就没有了,head就位空了。
            r.queue = NULL;//标记已经出对
            r.next = r;//下一个节点删除引用
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }

    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }

    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);//等待这么长时间,等待没有结束有可能被唤醒了//如果 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;//修改开始时间
                }
            }
        }
    }

    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);//不等待
    }

    void forEach(Consumer<? super Reference<? extends T>> action) {
        for (Reference<? extends T> r = head; r != null;) {
            action.accept(r);//头结点开始
            Reference<? extends T> rn = r.next;//下一个节点
            if (rn == r) {//最后一个节点
                if (r.queue == ENQUEUED) {//最后一个节点还处于入队状态
                    r = null;//推出循环
                } else {//最后一个节点r.queue == NULL,设置头结点。
                    r = head;
                }
            } else {//不是最后一个节点
                r = rn;
            }
        }
    }
}

技术图片

技术图片

 public class SimpleMonitorClassLoader {
    public static void main(String args[]) throws Exception{
        final ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
        final Map<Object, Object> map = new HashMap<>();
        @SuppressWarnings({ "unchecked", "rawtypes" })
        Thread thread = new Thread(() -> {
            try {
                WeakReference<Object> k;
                while((k = (WeakReference) rq.remove()) != null) {//通过remove方法查看队列的元素,remove时候,如果队列没有元素,就会阻塞等到Reference的tryHandlePending去放元素//垃圾回收器去调用tryHandlePending放元素入队。
                    System.out.println("GC回收了:" + map.get(k));
                    System.out.println("GC回收了:" + k);//队列里面是WeakReference。真正的对象b1b2b3已经置位null了,WeakReference本身是强引用要回收。
//                    GC回收了:weakReference
//                    GC回收了:[email protected]
//                    GC回收了:weakReference1
//                    GC回收了:[email protected]
//                    GC回收了:weakReference2
//                    GC回收了:[email protected]

                }
            } catch(InterruptedException e) {
            }
        });
        thread.setDaemon(true);
        thread.start();
        
        B b1 = new B();
        B b2 = new B();
        B b3 = new B();
        
        WeakReference<B> weakReference = new WeakReference<B>(b1, rq);
        map.put(weakReference, "weakReference");
        WeakReference<B> weakReference1 = new WeakReference<B>(b2, rq);
        map.put(weakReference1, "weakReference1");
        WeakReference<B> weakReference2 = new WeakReference<B>(b3, rq);
        map.put(weakReference2, "weakReference2");
        
        b1=null;
        b2=null;//b1,b2为null了,2个weakReference就被垃圾回收器加入队列中去。
        for(int i=0;i<5;i++) {
            System.gc();//促使垃圾回收器去回收,也即是促使weakReference,weakReference1,weakReference2加入ReferenceQueue队列。然后remove方法就会得到。
                   //Reference的tryHandlePending方法就是把WeakReference放入到队列里面去。
        }
        b3=null;//最后回收b3的,不置为null,外面的WeakReference就不会加到队列里面去。
        System.gc();
        Thread.sleep(1000000);
    }
}
class B {  
      
}  

 

以上是关于WeakReference Reference ReferenceQueue的主要内容,如果未能解决你的问题,请参考以下文章

java中SoftReference与WeakReference应用于高速缓存示例

JDK源码分析之 Reference 实现和应用

java WeakReference

weakReference 讲解

基础-WeakReference

说说WeakReference弱引用