美文网首页
Reference、ReferenceQueue

Reference、ReferenceQueue

作者: sollian | 来源:发表于2018-06-23 14:47 被阅读44次

关于Java源码的学习可以参考《Java源码分析》:ReferenceQueue、Reference及其子类。这里看一下Android api27源码中的引用相关的类。

学习之前我们做个约定:

Reference ref = new WeakReference<String>("hello");

我们称ref引用对象"hello"实际对象

Reference

Reference类与GC有紧密联系,不要直接继承Reference类
Reference类中部分代码是通过GC操作的,我们只关注表层的内容。涉及到的代码如下:

    volatile T referent;         /* Treated specially by GC ,由GC操作*/
    final ReferenceQueue<? super T> queue;

    Reference queueNext;

    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = queue;
    }

    public T get() {
        return getReferent();
    }

    public void clear() {
        clearReferent();
    }

    public boolean isEnqueued() {
        return queue != null && queue.isEnqueued(this);
    }

    public boolean enqueue() {
       return queue != null && queue.enqueue(this);
    }

GC在清除实际对象时,若queue不为null,则会将该引用对象enqueue。

queueNext字段

Reference类本身可以理解为一个链表的节点。每个Reference对象只可以enqueue一次。
若该Reference对象已经enqueue,并且未被remove,则queueNext指向下一个Reference对象。若被remove,则queueNext=ReferenceQueue.sQueueNextUnenqueued,之后无法再次enqueue。

get()

返回该Reference的实际对象,若被释放,则返回null。
实际对象可能被GC释放,也可以通过调用clear()手动释放。

clear()

释放实际对象。该方法不会使引用对象enqueue。
该方法由java代码调用,GC释放实际对象不会调用该方法。

isEnqueued()

判断该引用对象是否已经enqueue。包括通过GC enqueue和手动调用enqueue()方法。

enqueue()

将该引用对象加入队列中。
该方法由java代码调用,GC引起的enqueue不会调用该方法。


ReferenceQueue

GC在检测到实际对象的可达性发生适当的变化后,会将引用对象追加到它所注册的ReferenceQueue中。

该类公共方法有:

    public ReferenceQueue() { }

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

            return reallyPollLocked();
        }
    }

    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 = reallyPollLocked();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            for (;;) {
                lock.wait(timeout);
                r = reallyPollLocked();
                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);
    }

除了第一个空的构造函数,剩下的3个都是弹出头结点的方法。poll是非阻塞方法,remove是阻塞方法。

那么如何保证每个Reference至多enqueue一次呢?

    //标识引用对象已经enqueue过的flag
    private static final Reference sQueueNextUnenqueued = new PhantomReference(null, null);

    private Reference<? extends T> reallyPollLocked() {
        if (head != null) {
            Reference<? extends T> r = head;
            if (head == tail) {
                tail = null;
                head = null;
            } else {
                head = head.queueNext;
            }

            // Update queueNext to indicate that the reference has been
            // enqueued, but is now removed from the queue.
            r.queueNext = sQueueNextUnenqueued;//每个出队的Reference,queueNext被设置为sQueueNextUnenqueued。
            return r;
        }

        return null;
    }

    private boolean enqueueLocked(Reference<? extends T> r) {
        // Verify the reference has not already been enqueued.
        if (r.queueNext != null) {//queueNext!=null表示是第二次enqueue请求
            return false;
        }
       ...
    }

关于SoftReference,Android源码中有这么一句:
避免使用软引用做缓存:
实际使用中,用软引用做缓存并不高效,运行时并没有太多的信息来决定哪些软引用该释放,哪些该保留。更加致命的是,运行时在面临清除软引用和增大堆尺寸的选择时,并不知道该怎么做。
对软引用的信息的缺乏限制了软引用的使用,过早释放会导致不必要的工作,过晚释放则会浪费内存。
应用应该使用LruCache来替代软引用,它有更高效的回收策略。


最后,关于开头提到的文章中的例子做个修改,以更全面了解Reference:

public class JavaDemo {
    private static final ReferenceQueue<Data> REFERENCE_QUEUE = new ReferenceQueue<>();

    private static void checkQueue() throws InterruptedException {
        Reference<? extends Data> inq = REFERENCE_QUEUE.remove();
        if (inq != null) {
            System.out.println("queue---" + inq.getClass().getSimpleName());
        }
    }

    public static void main(String[] args) {
        //另起一个线程监视REFERENCE_QUEUE
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        checkQueue();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.setDaemon(true);
        thread.start();

///////////////////////////////////////////////////////////////////////////////////
        /*
         * SoftReference:在内存不足时才会回收这样软引用对象
         * */
        Deque<SoftReference<Data>> sa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            sa.add(new MySoftReference(new Data("Soft " + i), REFERENCE_QUEUE));
            System.out.println("created---" + sa.getLast().get());
        }

        //由于无法触发内存不足,这里手动回收软引用。
        for (SoftReference<Data> reference : sa) {
            reference.clear();
            //clear()方法不会enqueue,所以需要手动入队。
            reference.enqueue();
        }

///////////////////////////////////////////////////////////////////////////////////
        /*
         * WeakReference:在GC发现只具有弱引用的对象会立即对其会回收
         * */
        Deque<MyWeakReference<Data>> wa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            wa.add(new MyWeakReference(new Data("Weak " + i), REFERENCE_QUEUE));
            System.out.println("created---" + wa.getLast().get());
        }

///////////////////////////////////////////////////////////////////////////////////
        Deque<MyPhantomReference<Data>> pa = new LinkedList<>();
        for (int i = 0; i < 5; i++) {
            pa.add(new MyPhantomReference(new Data("Phantom " + i), REFERENCE_QUEUE));
            System.out.println("created---" + pa.getLast());
        }

///////////////////////////////////////////////////////////////////////////////////
//        System.gc(); //第一次gc
///////////////////////////////////////////////////////////////////////////////////
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        System.gc();//第二次gc
///////////////////////////////////////////////////////////////////////////////////
//        sa.clear();
//        pa.clear();
//        wa.clear();
//        System.gc();//第三次gc
///////////////////////////////////////////////////////////////////////////////////
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        System.gc();//第四次gc
    }
}

class MySoftReference<T> extends SoftReference<T> {

    public MySoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

    @Override
    protected void finalize() {
        System.out.println("finalize---MySoftReference");
    }
}

class MyWeakReference<T> extends WeakReference<T> {
    MyWeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

    @Override
    protected void finalize() {
        System.out.println("finalize---MyWeakReference");
    }
}

class MyPhantomReference<T> extends PhantomReference<T> {

    MyPhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

    @Override
    protected void finalize() {
        System.out.println("finalize---MyPhantomReference");
    }
}

class Data {
    private final String id;

    Data(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return id;
    }

    @Override
    protected void finalize() {
        System.out.println("finalize---" + id);
    }
}

此时输出如下:

created---Soft 0
created---Soft 1
created---Soft 2
created---Soft 3
created---Soft 4
queue---MySoftReference
queue---MySoftReference
queue---MySoftReference
queue---MySoftReference
queue---MySoftReference
created---Weak 0
created---Weak 1
created---Weak 2
created---Weak 3
created---Weak 4
created---demo.sollian.com.demo.MyPhantomReference@677327b6
created---demo.sollian.com.demo.MyPhantomReference@14ae5a5
created---demo.sollian.com.demo.MyPhantomReference@7f31245a
created---demo.sollian.com.demo.MyPhantomReference@6d6f6e28
created---demo.sollian.com.demo.MyPhantomReference@135fbaa4

除了手动将软引用对象clear并enqueue,其他的看不到对象回收的迹象。

接下来打开第一次gc,

        System.gc(); //第一次gc

新增的输出如下:

...
finalize---Soft 0
finalize---Soft 1
finalize---Soft 2
finalize---Weak 2
finalize---Phantom 4
finalize---Phantom 3
finalize---Phantom 2
finalize---Phantom 1
finalize---Phantom 0
finalize---Weak 4
finalize---Weak 3
finalize---Weak 1
finalize---Weak 0
finalize---Soft 4
finalize---Soft 3
queue---MyWeakReference
queue---MyWeakReference
queue---MyWeakReference
queue---MyWeakReference
queue---MyWeakReference

可以看到软引用、弱引用、虚引用的实际对象都已被回收,执行了finalize方法;并且弱引用对象enqueue。

打开第二次gc:

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();//第二次gc

新增的输出如下:

...
queue---MyPhantomReference
queue---MyPhantomReference
queue---MyPhantomReference
queue---MyPhantomReference
queue---MyPhantomReference

此时虚应用对象enqueue。
至此,对实际引用的释放处理已经彻底走完。

接下来释放引用对象。打开第三次gc:

        sa.clear();
        pa.clear();
        wa.clear();
        System.gc();//第三次gc

新增的输出如下:

...
finalize---MyWeakReference

可以看到释放了一个弱引用对象。

打开第四次gc:

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();//第四次gc

新增的输出如下:

...
finalize---MySoftReference
finalize---MySoftReference
finalize---MySoftReference
finalize---MySoftReference
finalize---MyWeakReference
finalize---MyWeakReference
finalize---MyWeakReference
finalize---MySoftReference
finalize---MyWeakReference
finalize---MyPhantomReference
finalize---MyPhantomReference
finalize---MyPhantomReference
finalize---MyPhantomReference
finalize---MyPhantomReference

可以看到释放了其他的引用对象。

相关文章

网友评论

      本文标题:Reference、ReferenceQueue

      本文链接:https://www.haomeiwen.com/subject/eygkyftx.html