reference类的有几个很重要的类:FinalReference, SoftReference,WeakReference、PhantomReference.
FinalReference:这个有且仅有一个子类,Finalizer,这个类是和Object的finalize方法(垃圾回收器回收对象前调用的方法)有关。
SoftReference: 软引用,当软引用包装的对象只剩下软引用一个引用的时候,当内存充足的时候gc不会对其进行回收。
WeakReference:弱引用,当软引用包装的对象只剩下软引用一个引用的时候,,只要gc发现了这个引用,就会进行回收。
PhantomReference:幽灵引用,或者说虚引用。这个引用对其包装的对象没有任何印象,get方法也是直接返回null,好像和包装的对象没有任何关联一样。这个引用的用法也和其他的引用不同。这个引用主要是在gc回收对象的时候,给用户一个通知。
这几个子类都和Reference父类有着密切的关系。
Reference的部分源代码
成员变量
public abstract class Reference<T> {
//引用包装的对象,用户传入的
private T referent; /* Treated specially by GC */
// 引用队列,用于记录引用状态,以及对引用链表队列处理
volatile ReferenceQueue<? super T> queue;
// reference链表队列的下一个引用
Reference next;
// gc操作的变量
transient private Reference<T> discovered; /* used by VM */
// 锁
static private class Lock { }
private static Lock lock = new Lock();
// gc操作的变量,如果包装的对象要进行gc回收,则会将这个引用的pendig对象赋值
private static Reference<Object> pending = null;
}
ReferenceQueue:Reference对象维护了一个ReferenceQueue队列,这个队列的入队操作的目的是记录一个垃圾回收Reference包装的对象的通知,比如PhantomReference幽灵引用,则需要用户传入自己的ReferenceQueue,然后从ReferenceQueue中获取垃圾回收的标识
ReferenceQueue还逻辑上分几种,用来标识Reference的状态:
ENQUEUED:已入队
NULL: 未入队
等。。。
ReferenceQueue的部分源代码
public class ReferenceQueue<T> {
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false;
}
}
// 逻辑意义为null的引用队列,Reference对象的quene对象为这个Null的时候,不进行入队操作
static ReferenceQueue<Object> NULL = new Null<>();
// 逻辑意义为已经入队的标识符
static ReferenceQueue<Object> ENQUEUED = new Null<>();
// 入队操作,真正的队列是Reference的next引用所维护的
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
synchronized (lock) {
// Check that since getting the lock this reference hasn't already been
// enqueued (and even then removed)
ReferenceQueue<?> queue = r.queue;
// reference对象的queue对象为逻辑null则不进行入队处理
if ((queue == NULL) || (queue == ENQUEUED)) {
return false;
}
assert queue == this;
r.queue = ENQUEUED;
r.next = (head == null) ? r : head;
head = r;
queueLength++;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(1);
}
lock.notifyAll();
return true;
}
}
}
// 出队操作
public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
Reference对象有一个静态代码块:会启动一个守护线程,来处理引用的入队等操作。
画外音:守护线程和用户线程的区别是,有用户线程存在的时候,jvm不会退出,会继续运行用户线程,即使启动它的main主线程运行完毕了。守护线程则不会影响jvm的退出。
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
// provide access in SharedSecrets
SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
@Override
public boolean tryHandlePendingReference() {
return tryHandlePending(false);
}
});
}
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) {
// gc回收引用包装的对象的时候,会给引用的pending变量设置,所以pending不会null,则说明gc回收了引用包装的对象
if (pending != null) {
r = pending;
// 'instanceof' might throw OutOfMemoryError sometimes
// Cleanner是PhantomReference的子类,用于回收堆外内存的
// 堆外内存所对应的java堆对象被gc回收了后,cleanner受到通知,清除堆外内存
// so do this before un-linking 'r' from the 'pending' chain...
c = r instanceof Cleaner ? (Cleaner) r : null;
// unlink 'r' from 'pending' chain
pending = r.discovered;
r.discovered = null;
} else {
// The waiting on the lock may cause an OutOfMemoryError
// because it may try to allocate exception objects.
if (waitForNotify) {
lock.wait();
}
// retry if waited
return waitForNotify;
}
}
} catch (OutOfMemoryError x) {
// Give other threads CPU time so they hopefully drop some live references
// and GC reclaims some space.
// Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
// persistently throws OOME for some time...
Thread.yield();
// retry
return true;
} catch (InterruptedException x) {
// retry
return true;
}
// Fast path for cleaners
if (c != null) {
// 回收堆外内存,调用的是unsafe.freeMemory方法
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
// 如果Reference的队列对象为逻辑null,则说明不需要入队操作,不需要发送给用户的通知,直接不处理
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
看唯一不同的幽灵引用的使用:自定义队列,然后gc回收的时候,referenceHandler守护线程会发现,引用的pending被gc设置值了,不为null,则进行处理,又发现引用的队列对象不为逻辑null,则将引用加入队列,所以用户可以从队列中获取到值。
public class TestPhantomReference {
private static ReferenceQueue<Object> rq = new ReferenceQueue<Object>();
public static void main(String[] args){
Object obj = new Object();
PhantomReference<Object> pr = new PhantomReference<Object>(obj, rq);
// 输出null
System.out.println(pr.get());
obj = null;
System.gc();
// 输出null
System.out.println(pr.get());
Reference<Object> r = (Reference<Object>)rq.poll();
// r不为空
if(r!=null){
System.out.println("回收");
}
}
}