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属性
// 用于标识没有注册Queue
static 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;
}
// ...
}