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 {  

}  

原文地址:https://www.cnblogs.com/yaowen/p/10841683.html

时间: 2024-11-05 14:02:15

WeakReference Reference ReferenceQueue的相关文章

Java Reference &amp; ReferenceQueue一览

Overview The java.lang.ref package provides more flexible types of references than are otherwise available, permitting limited interaction between the application and the Java Virtual Machine (JVM) garbage collector. It is an important package, centr

WeakReference &amp;&amp;reference quene &amp;&amp;GC

在了解WeakReference之前,先给出一段简单的代码: public class WeakReferenceTest {public static void main(String[] args) throws Exception {Object o = new Object();// 默认的构造函数,会使用ReferenceQueue.NULL 作为queueWeakReference<Object> wr = new WeakReference<Object>(o);Sy

Java Reference 源码分析

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

JDK 源码阅读 Reference

Java最初只有普通的强引用,只有对象存在引用,则对象就不会被回收,即使内存不足,也是如此,JVM会爆出OOME,也不会去回收存在引用的对象. 如果只提供强引用,我们就很难写出"这个对象不是很重要,如果内存不足GC回收掉也是可以的"这种语义的代码.Java在1.2版本中完善了引用体系,提供了4中引用类型:强引用,软引用,弱引用,虚引用.使用这些引用类型,我们不但可以控制垃圾回收器对对象的回收策略,同时还能在对象被回收后得到通知,进行相应的后续操作. 引用与可达性分类 Java目前有4中

由Reference展开的学习

在阅读Thinking in Java的Containers in depth一章中的Holding references时,提到了一个工具包java.lang.ref,说这是个为Java垃圾回收提供了很大的灵活性的包. 并引出了抽象类Reference还有它的三个子类,书上看了好几次都一脸懵逼--最后百度了很久现在简单记录总结一下. 一.为什么要有这个Reference类呢? 一般的对象在程序中,都是可获取的,也就是有直接引用的,或者用更专业的词(等等会介绍)就是强引用的对象.一般对于这样可以

利用 LeakCanary 来检查 Android 内存泄漏

前言 你被概率性的 OOM 困扰么?有时候,OOM 像幽灵一样,挥之不去,可真想把它揪出来时,又捉之不着.或许,是时候用 LeakCanary 来诊断一下了.它是一个用来检查 Android 下内存泄漏的开源库,这篇文章主要介绍其用法.架构和其背后的实现原理. Square 有篇文章介绍了开发这个库的原因.他们的一个付款流程里,需要用到用户的签名,他们直接用 Bitmap 来画签名,Bitmap 大小和屏幕分辨率是一样的.问题来了,在试图创建这个 Bitmap 对象时,概率性 OOM 如幽灵般相

[Java][Android][Process] Process 创建+控制+分析 经验浅谈

不管是Android亦或者Java中或多或少须要调用底层的一些命令.运行一些參数: 此时我们须要用到Java的Process来创建一个子进程.之所以是子进程是由于此进程依赖于发起创建请求的进程,假设发起者被Kill那个子进程也将Kill. 对于Process相信使用过的朋友一定不会陌生,它具有例如以下特点: 1.创建简单 2.控制难 3.easy导致无法创建子进程 4.假设是多线程那么非常有可能造成内存溢出 以上现象假设你仅仅是偶尔使用一次,创建一个进程也许你什么都没有感觉到,可是假设你使用了多

Java强引用、软引用、弱引用、虚引用详解

***********************************************声明****************************************************** 原创作品,出自 "晓风残月xj" 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj). 由于各种原因,可能存在诸多不足,欢迎斧正! *******************************************

闲来无事,用Java的软引用写了一个山寨的缓存

闲来无事,用Java的软引用写了一个山寨的缓存 博客分类: java基础 众所周知java中的引用分为 StrongReference.SoftReference.WeakReference.PhantomReference.这几种引用有不同那个的 使用场景,平时我们用的最频繁的也就是StrongReference也就是说形如之这样的引用: Object obj = new Object(); 这种引用就是所谓的强引用,如果此对象没有引用指向它,并且活着的线程无法访问到它(针对垃圾孤岛而言),那