Java源码:Reference与ReferenceQueue

一:Reference与ReferenceQueue

Reference的四种状态:
  1. Active:活跃,内存一开始分配的常有状态,垃圾收集器进行对该引用可达性分析后会进入Pending或Inactive状态,这取决于引用创建的时候是否绑定到一个ReferenceQueue。
  2. Pending:待定,进行回收时,会先进入pending-Reference队列,等待着Reference-handler线程来取走入队。
  3. Enqueued:入队,内存已回收的对象,若从队列中删除,则转为Inactive状态。
  4. Inactive:不活跃,最终状态,不可改变。
回收过程引用状态变化:
            ReferenceQueue queue = new ReferenceQueue();
            //一开始创建的时候,reference状态为Avtice
            WeakReference reference = new WeakReference<>(new Object(),queue);
            //要进行回收了,进入Pending状态,在Reference-handler队列中等待ReferenceHandler线程过来取走
            //ReferenceHandler线程是垃圾回收器的线程
            //取走引用,加入ReferenceQueue中,状态改为Enqueued
            System.gc();
            try {
                //从队列中取走引用,状态改为Inactive
                Reference remove = queue.remove();
            }catch (Exception e){
                e.printStackTrace();
            }
ReferenceQueue:

它就是一个引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到Pending队列中,ReferenceQueue实现对引用的入队(enqueue)和出队(poll),remove操作。

Reference构造方法:
   Reference(T referent) {
        this(referent, null);
    }
    
    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
	//ReferenceQueue.NULL:继承ReferenceQueue
	//重写enqueue方法,直接返回false,起到状态表示作用
    private static class Null<S> extends ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }
Reference成员变量:
  • private T referent;
    由构造器赋值,指的是传入java对象的引用

  • private static Reference pending = null;
    Pending队列,由JVM的垃圾回收器赋值,当对象除了被reference引用之外没有其它强引用了,jvm的垃圾回收器就会将指向需要回收的对象的Reference都放入到这个队列里面,Reference指的是Reference的成员变量。内存回收时,先加入该队列,等待Reference-handler线程来进行remove,加入ReferenceQueue中,并使用discovered字段来链接它的数据。

  • transient private Reference discovered
    表示要处理的对象的下一个对象,由垃圾回收器进行赋值。

  • volatile ReferenceQueue<?super T> queue;
    ReferenceQueue队列,但它只持有这个链表的表头对象header,这个链表是由Refence对象里面的next成员变量构建起来的,next也就是链表当前节点的下一个节点,所以Reference对象本身就是一个链表的节点。

  • Reference next;
    用来与queue成员变量一同组成ReferenceQueue队列

  • static private class Lock { };

  • private static Lock lock =new Lock();
    lock成员变量是pending队列的全局锁,ReferenceHander线程run方法里面用到pending队列。

静态代码块:
    static {
    	//获得当前线程的所有线程
        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);
            }
        });
    }

当 Refrence 类被加载的时候,会创建启动 ReferenceHandler 线程,设为守护线程并设置线程的级别为最大级别。

现在看一个ReferenceHandler 线程的run方法

 private static class ReferenceHandler extends Thread {
    	//...
        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
   }
   static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
            //检查pengding是否为空
                if (pending != null) {
                    r = pending;
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    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);
        return true;
    }

在 tryHandlePending 方法里面,检查 pending 是否为 null,如果pending不为 null,则将 pending 进行 enqueue,否则线程进入 wait 状态,discovered和 pending 是由垃圾回收器进行赋值的。

    boolean enqueue(Reference<? extends T> r) { 
        synchronized (lock) {
            ReferenceQueue<?> queue = r.queue;
            // queue 为 null 或者 queue 已经被回收了,直接返回
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;
            //设置状态
            r.queue = ENQUEUED;
           // 将 refrence 进行前插入
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

二:Refrence 的子类

  1. StrongReference
    java没有这个类,但强引用比较普遍,若果一个对象是强引用,那回收器则始终都不会回收该对象。
  2. SoftReference
    如果一个对象只有软引用,则在内存充足的情况下是不会回收此对象的,但是,在内部不足即将要抛出OOM异常时就会回收此对象来解决内存不足的问题。
  3. WeakReference
    只要 GC 发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收。
  4. PhantomReference
    PhantomReference,即虚引用,虚引用并不会影响对象的生命周期。虚引用的作用为:跟踪垃圾回收器收集对象这一活动的情况。
    当GC一旦发现了虚引用对象,则会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference对象并没有被垃圾回收器回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。
    注意:PhantomReference必须要和ReferenceQueue联合使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值