一.Reference类型(4种类型引用)
1.StrongReference(强引用):垃圾回收器不会回收释放该对象,需要自己去释放。当内存不足时就直接抛出OOM。
2.SofeReference(软引用):当内存不足时,触发GC会释放对象内存,内存足够即使GC也不会释放该对象。
3.WeakReferecne(弱引用):当触发GC时就会释放对象内存。
- PhantomReference(虚引用):具体也不知道用来干嘛。
二.WeakReference和ReferenceQueue关系
引用队列在垃圾回收器回收对象的时候,会将已经注册的对象添加到引用队列中。我们可以通过引用队列判断该对象是否已经释放掉。上篇分析的LeakCanary原理就是利用这点。
当需要检测某个对象是否被回收,我们可以将Reference关联到ReferenceQueue上,具体如下:
ReferenceBean o = new ReferenceBean();
ReferenceQueue weakReferenceQueue = new ReferenceQueue();
WeakReference weakReference = new WeakReference<>(o,weakReferenceQueue);
三.Reference状态
源码:java.lang.ref.Reference
volatile ReferenceQueue super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
@SuppressWarnings("rawtypes")
Reference next;
active:内存被分配的时候状态。
pending:即将回收内存,存入关联的引用queue中时的状态。
Enqueued:内存被回收的时候,进入引用队列中的状态。
Inactive:最终不活跃状态,该状态不会转换成其他状态。
四.Reference与Referencequeue源码分析
1.Reference
源码:java.lang.ref.Reference
我们首先看看有哪些成员对象
private T referent; /* Treated specially by GC */
volatile ReferenceQueue super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
@SuppressWarnings("rawtypes")
Reference next;
/* When active: next element in a discovered reference list maintained by GC (or this if last)
* pending: next element in the pending list (or null if last)
* otherwise: NULL
*/
transient private Reference discovered; /* used by VM */
/* Object used to synchronize with the garbage collector. The collector
* must acquire this lock at the beginning of each collection cycle. It is
* therefore critical that any code holding this lock complete as quickly
* as possible, allocate no new objects, and avoid calling user code.
*/
static private class Lock { }
private static Lock lock = new Lock();
/* List of References waiting to be enqueued. The collector adds
* References to this list, while the Reference-handler thread removes
* them. This list is protected by the above lock object. The
* list uses the discovered field to link its elements.
*/
private static Reference
referent:表示引用对象。
queue:关联的引用队列。
next:下一个即将被处理的节点。
discovered:要处理的下一个对象。
pending:等待被入队列的引用列表。
当Reference被加载的时候,就好执行Reference类中的静态代码块,所以我们先从Reference类静态代码块开始分析:
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);
}
});
}
静态代码块中其实启动了一个线程,然后priority优先级为:MAX_PRIORITY,然后设置是否是后台驻留线程,启动。
然后我们接着分析ReferenceHandler这个线程:
private static class ReferenceHandler extends Thread {
private static void ensureClassInitialized(Class> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
// pre-load and initialize InterruptedException and Cleaner classes
// so that we don't get into trouble later in the run loop if there's
// memory shortage while loading/initializing them lazily.
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
看run方法,一个死循环,我们再跟进tryHandlePending方法。
static boolean tryHandlePending(boolean waitForNotify) {
Reference
当pending不为null的时候,penging上面提到过是等待被入队列的引用列表,看最后两行代码就是加入到引用队列中。当pending为null时会一直处于wait状态。
那WeakReference和ReferenceQueue是怎样关联的呢,看最后一行代码if (q != ReferenceQueue.NULL) q.enqueue(r)
接下来我们分析下ReferenceQueue中enqueue方法:
源码:java.lang.ref.ReferenceQueue
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;
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;
}
}
首先判断queue是否为空或者是否为ENQUEUED状态,如果是直接返回。
如果当queue不为空时,将queue置为ENQUEUED状态,表示已经回收。
然后将Reference r插入到队列的头部。
至此Reference和ReferenceQueue源码就分析完了。