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;//标记已经出对,NULL也是一个ReferenceQueue,只不过入队方法为false了,r再次被gc时候,加入到NULL这个队列,就会返回false,此时便会回收r。 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回收了:java.lang.ref.WeakReference@1b604f19 // GC回收了:weakReference1 // GC回收了:java.lang.ref.WeakReference@7823a2f9 // GC回收了:weakReference2 // GC回收了:java.lang.ref.WeakReference@4cc0edeb } } 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就被垃圾回收器加入队列中去。
//虽然弱引用无论内存充足都会回收仅仅被这个弱引用关联的他里面的强引用对象,但是b1,b2不置位null,那么这个弱引用里面的强引用就不仅仅是被这个弱引用关联,还被b1 b2关联,所以不会回收,下面的例子会回收。 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 { }
public class Test { @SuppressWarnings("rawtypes") public static void main(String[] args) { A a = new A(); Object obj = null; ReferenceQueue queue = new ReferenceQueue();// WeakReference ref = new WeakReference(a, queue);// 此时queue里面元素的长度为0,referent=A System.out.println(ref.get());// ssss.A@1554909b a = null; System.out.println(ref.get());// ssss.A@1554909b,还没来得及gc System.gc(); System.out.println(ref.get());// null,gc之后,WeakReference里面的内容被回收了,但是WeakReference本身是强引用还在,queue里面已经有一个元素了,就是WeakReference自己,
//此时WeakReference的referent=null,就回去排队。 obj = queue.poll();//此时WeakReference的referent已经为空。queue出队之后就没有元素了。 System.out.println(obj);//java.lang.ref.WeakReference@4590c9c3 } } class A { } // 分析,在GC运行时,检测到new A()生成的对象只有一个WeakReference引用着,所以决定回收它(a = null,已经没有强引用指向了), // 首先清除 WeakReference的referent = null,然后referent的状态为finalizable, // 同时或者一段时间后把WeakReference放入监听的ReferenceQueue中。
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回收了:c{C,referent=null,value="b1"} // GC回收了:ssss.C@457e2f02 // GC回收了:c1{C,referent=null,value="b2"} // GC回收了:ssss.C@5c7fa833 // GC回收了:c2{C,referent=null,value="b3"} // GC回收了:ssss.C@39aeed2f } } catch(InterruptedException e) { } }); thread.setDaemon(true); thread.start(); B b1 = new B(); B b2 = new B(); B b3 = new B(); C c = new C(b1,"b1", rq); map.put(c, "weakReference"); C c1 = new C(b2,"b2" ,rq); map.put(c1, "weakReference1"); C c2 = new C(b3,"b3", rq); map.put(c2, "weakReference2"); b1=null; b2=null;//b1,b2为null了,2个C就被垃圾回收器去排队。b1 b2要置位null,不然不会回收和排队 for(int i=0;i<5;i++) { System.gc();//促使垃圾回收器去回收,也即是促使c,c1,c2加入ReferenceQueue队列。然后remove方法就会得到。 } b3=null;//最后回收b3的 System.gc();//c2去排队 Thread.sleep(1000000); } } class C extends WeakReference{ Object value; C(B k, Object v,ReferenceQueue q) {//只要k置位null了,C就会去排队。 super(k,q); value = v; } } class B{ }
public class WeakReferenceDemo { public static void main(String[] args) throws InterruptedException{ final ReferenceQueue<Object> rq = new ReferenceQueue<Object>(); /*若引用对象中指向了一个长度为1000个元素的整形数组*/ WeakReference<String[]> weakReference = new WeakReference<String[]>(new String[1000],rq); /*未执行gc,目前仅被弱引用指向的对象还未被回收,所以结果不是null,[Ljava.lang.String;@15db9742*/ System.out.println(weakReference.get()); // WeakReference<Object> k = (WeakReference) rq.remove();//没有元素 /*执行一次gc,即使目前JVM的内存够用,但还是回收仅被弱引用指向的对象new String[1000]*/ System.gc(); System.out.println(weakReference.get());// null。这个弱引用里面的强引用仅仅被这个弱引用关联,所以一旦gc就会去回收这个强引用, System.out.println(weakReference);//java.lang.ref.WeakReference@6d06d69c WeakReference<Object> k1 = (WeakReference) rq.remove();//有元素 } } //同理,上面的代码中名为weakReference的引用指向了一个 //WeakReference对象,这个指向还是一个强引用类型。而WeakReference对象中指向String类型数组的引用就是一个弱引用类型了。 //new String[1000] 就变成了一个弱引用,内存不足时候就会回收new String[1000]。 不是weakReference是一个弱引用,weakReference是一个强引用。是他里面的强引用变成了一个弱引用。强引用内存只有弱引用关联,没有其他变量关联时候,gc就会去回收这个强引用。有别的对象关联就不会去回收这个强引用。 //当JVM进行垃圾回收时,无论内存是否充足,都会回收仅被弱引用关联的对象。所以new String[1000]会被回收,同时weakReference会去排队。