一.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<ReferenceBean> 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<T> 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<Object> pending = null;
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<Object> r;
Cleaner c;
try {
synchronized (lock) {
if (pending != null) {
r = pending;
// 'instanceof' might throw OutOfMemoryError sometimes
// 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) {
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
当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源码就分析完了。
网友评论