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的主要内容,如果未能解决你的问题,请参考以下文章