引用类型
SoftReference
普通的GC不会回收软引用,只有在即将发生OOM的时候(即最后一次Full GC),如果被引用的对象只有SoftReference指向的引用,才会被回收.
WeakReference
当发生GC时,如果被引用的对象只有WeakReference指向的引用,就会被回收
PhantomReference
不能通过虚引用获取到其关联的对象,但是当GC发生时,如果其引用的对象被回收,这个事件是可以感知的,这样就可以做相应的处理.
强引用
GC是如何决定一个对象是否被回收?
从GC Root开始向下搜索,如果对象与GC之间存在引用链,则对象是可达的(reachable),GC会根据是否可达,以及对象的可达性来决定对象是否可以被回收.
而对象的可达性与引用类型相关:
- 强可达
- 软可达
- 虚可达
- 弱可达
- 不可达
Refercence的几种状态
一个Reference对象可能处于以下4种状态中的一种:
Active
新创建的对象都是处于这个状态;
GC检测到引用的对象可达性改变时,会将状态转变成Pending或者Inactiive(如果refercence创建时带了referenceQueue,则会将refercence加入到pending 引用队列中)
queue = 如果创建Refercence时注册了queue,则对应那个queue,如果没有,则为RefercenceQueue.NULL next = null
Pending
马上要被放进队列中的对象,即即将被回收的对象
pending引用队列中的一个元素,等着被ReferenceHandler线程(一个守护线程)去enqueue.
没有注册(即创建时没有带queue的)的对象不会处于这个状态;
queue = 注册时的RefercenceQueue next = this
Enqueued
对象的内存已经被回收了,已经把这个对象放到了一个队列中
当一个对象从它的引用队列中移除时,它就是Inactive状态了,未注册的对象不会处于这个状态;
queue = RefercenceQueue.ENQUEUED next = queue中下一个对象,或者this(这是最后一个元素)
Inactive
最终状态,不会再改变了.
queue = RefercenceQueue.NULL next = this
处理流程
JVM在进行GC的时候,如果当前对象只被Reference对象引用,JVM会根据Reference的具体类型和堆内存的使用情况来决定:
是否把对应的Reference对象加入到一个由Reference构成的pending链表上(即Refercence中的next属性).
如果能加入到pending链表上,则JVM会同时通知ReferenceHandler线程进行处理.
ReferenceHandler线程是在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);
}
});
}
再来看看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 {
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
tryHandlePending(true);
}
}
}
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;
}
ReferenceHandler线程内部的run方法,会不断从pending链表中获取Refercence对象,
如果能获取到,则根据不同的引用类型进行处理;
如果获取不到,则调用wait方法等待GC回收对象,处理pending链表的通知.