java集合之4种Reference

来源:互联网 发布:网络异常请重新登录 编辑:程序博客网 时间:2024/05/29 03:47

public abstract class Reference<T> {

public class SoftReference<T> extends Reference<T> {

public class WeakReference<T> extends Reference<T> {

public class PhantomReference<T> extends Reference<T> {

可以用以下表格总结: 

级别

什么时候被垃圾回收

用途

生存时间

强引用

从来不会

对象的一般状态

JVM停止运行时终止

软引用

在内存不足时

缓存

内存不足时终止

弱引用

在垃圾回收时

对象缓存

gc运行后终止

虚引用

Unknown

Unknown

Unknown

 


Reference:

Reference内部有几个比较重要的属性

// 用于保存对象的引用,GC会根据不同Reference来特别对待private T referent;// 如果需要通知机制,则保存的对对应的队列ReferenceQueue<? super T> queue;/* 这个用于实现一个单向循环链表,用以将保存需要由ReferenceHandler处理的引用 */Reference next;static private class Lock { };// 锁,用于同步pending队列的进队和出队private static Lock lock = new Lock();// 此属性保存一个PENDING的队列,配合上述next一起使用private static Reference pending = null;

内部类ReferenceHandler

ReferenceHandler作为Reference的静态内部类,用于实现将pending队列里面的Reference实例依次添加到不同的ReferenceQueue中(取决于Reference里面的queue)。该pending的元素由GC负责加入。
注:这里对pending队列进行加锁,个人认为是因为GC线程可能和ReferenceHandler所在的线程并发执行,如GC采用CMS并发收集的时候。
如下代码所示

// 此线程在静态块中启动,即一旦使用了Reference,则会启动该线程private static class ReferenceHandler extends Thread {    public void run() {        for (;;) {            Reference r;            synchronized (lock) {                if (pending != null) {                    r = pending;                    Reference rn = r.next;                    // 从pending中取下一个元素,如果后继为空,则next指向自身                    pending = (rn == r) ? null : rn;                    r.next = r;                } else {                    try {                        // 没有则等待,后续加入元素会调用lock.notify唤醒                        lock.wait();                    } catch (InterruptedException x) { }                    continue;                }            }            // ...            ReferenceQueue q = r.queue;            // 如果该Reference注册了对应的Queue,则加入到该Queue中            if (q != ReferenceQueue.NULL) q.enqueue(r);        }    }}

ReferenceQueue属性

// 用于标识没有注册Queuestatic ReferenceQueue NULL = new Null();// 用于标识已经处于对应的Queue中static ReferenceQueue ENQUEUED = new Null();static private class Lock { };/* 互斥锁,用于同步ReferenceHandler的enqueue和用户线程操作的remove和poll出队操作 */private Lock lock = new Lock();// 队列private volatile Reference<? extends T> head = null;// 队列中的元素个数private long queueLength = 0;

ReferenceQueue.enqueue

只会通过Reference里要调用该方法,用于将Reference放入到当前队列中

boolean enqueue(Reference<? extends T> r) {    synchronized (r) {        // 判断是否已经入队了        if (r.queue == ENQUEUED) return false;        synchronized (lock) {            r.queue = ENQUEUED;            // 单向循环            r.next = (head == null) ? r : head;            head = r;            queueLength++;            if (r instanceof FinalReference) {                sun.misc.VM.addFinalRefCount(1);            }            // 通知当前挂起的线程(调用remove时有可能会挂起)            lock.notifyAll();            return true;        }    }}

应用 - WeakHashMap

WeakHashMap在使用上和HashMap类型,都是Hash + 链表解决冲突,唯一不同点在于前者的Key是使用虚引用来实现的,即当进行垃圾回收的时候,就是被回收掉,此时WeakHashMap会在下次操作的时候,根据被回收掉的Key,从Map里面移除掉。

Entry

当创建Entry的时候,会注册进当前Map属性的queue,当key被回收后,则该Entry会被放入到queue中,每当操作Map的时候,才会将原有的Value清除掉。(由expungeStaleEntries方法来进行,并且没有启动一个单独的线程来处理,没有必要,这样子简化了逻辑以及避免锁的开销)

// 外部WeakHashMap属性private final ReferenceQueue<Object> queue = new ReferenceQueue<>();/* 这里采用了集成WeakReference而不是直接使用,是因为当被回收的时候,具体的Key是不知道的,这里需要往WeakReference额外加入一些属性,以便在被回收后通知时,能够定位到具体的Key/value */private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {    // 这里属性不能加入key,否则会导致存在强引用而不能被视为WeakReference回收掉    V value;    int hash;    Entry<K,V> next;        Entry(Object key, V value,          ReferenceQueue<Object> queue,          int hash, Entry<K,V> next) {        super(key, queue);        this.value = value;        this.hash  = hash;        this.next  = next;    }    // ...}





原创粉丝点击