Reference源码分析
Reference
对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互。即可以使用Reference对象来引用其它对象,但是最后还是会被垃圾收集器回收。程序有时候也需要在对象回收后被通知,以告知对象的可达性发生变更。
Java提供了四种不同类型的引用,引用级别从高到低分别为FinalReference
,SoftReference
,WeakReference
,PhantomReference
。其中FinalReference
不对外提供使用。每种类型对应着不同级别的可达性。
简介
强引用FinalReference
强引用指的是,程序中有直接可达的引用,而不需要通过任何引用对象,如Object obj = new Object();中,obj为强引用。
软引用SoftReference
软引用,非强引用,但是可以通过软引用对象来访问。只有软引用的对象,只有在内存不足的时候(抛出OOM异常前),垃圾收集器会决定回收该软引用所指向的对象。软引用通常用于实现内存敏感的缓存。
SoftReference<Object> softRef = new SoftReference<Object>(new Object());
弱引用WeakReference
弱引用,非强引用和软引用,但是可以通过弱引用对象来访问。只有弱引用的对象,不管内存是否足够,只要被垃圾收集器发现,该引用的对象就会被回收。实际的应用见WeakHashMap
等。
WeakReference<Object> weakRef = new WeakReference<Object>(new Object());
虚引用PhantomReference
虚引用,该引用必须和引用队列(ReferenceQueue
)一起使用,一般用于实现追踪垃圾收集器的回收动作,比如在对象被回收的时候,会调用该对象的finalize方法,在使用虚引用可以实现该动作,也更加安全。
Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<Object>(obj, refQueue);
- 强可到达,如果从GC Root搜索后,发现对象与GC Root之间存在强引用链则为强可到达。强引用链即有强引用对象,引用了该对象。
- 软可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链,但存在软引用链,则为软可到达。软引用链即有软引用对象,引用了该对象。
- 弱可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链与软引用链,但有弱引用链,则为弱可到达。弱引用链即有弱引用对象,引用了该对象。
- 虚可到达,如果从GC Root搜索后,发现对象与GC Root之间只存在虚引用链则为虚可到达。虚引用链即有虚引用对象,引用了该对象。
- 不可达,如果从GC Root搜索后,找不到对象与GC Root之间的引用链,则为不可到达。
Reference
Reference对象有四种状态
Active:对象刚创建时便处于这种状态。
Pending:检测到引用的Object可达性状态发生改变时进入此状态,前提是实例化时,构造函数传入了ReferenceQueue.
Enqueued:对象被ReferenceHandler线程从Pending队列加入到关联的queue时
Inactive:
- 检测到引用的Object可达性状态发生改变时进入此状态,前提是实例化时,构造函数没有传ReferenceQueue.
- 从ReferenceQueue出队后
对象所处的状态通过内部成员变量next、queue组合起来确定。
Active:queue=ReferenceQueue.NULL/ReferenceQueue next=null
Pending:next=this queue=ReferenceQueue
Enqueued:queue = ReferenceQueue.ENQUEUED next=队列中的下一个Reference对象或者本身(当队列只有一个元素时)
Inactive: queue = ReferenceQueue.NULL; next = this
检测引用的Object可达性状态发生改变时垃圾回收线程实现,并将引用对象加入Pending队列。
从Pending队列进入关联的ReferenceQueue是ReferenceHandler线程实现的。
ReferenceQueue
引用对象在GC时检测到不可达时,会将对象注册到此队列中,前提时引用对象实例化时,指定了关联的队列(如下图所示)。
static ReferenceQueue<Object> NULL = new Null<>(); // 代表引用对象实例化时未关联队列
static ReferenceQueue<Object> ENQUEUED = new Null<>(); // 代表引用对象已经注册队列
private volatile Reference<? extends T> head = null; // 队列的头部元素 队列的数据结构是单向循环链表
private long queueLength = 0; // 队列的元素个数
入队操作
采用头插法添加到队列
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对象上阻塞的线程
lock.notifyAll();
return true;
}
}
出队操作
private Reference<? extends T> reallyPoll() { /* Must hold lock */
Reference<? extends T> r = head;
if (r != null) {
head = (r.next == r) ?
null : // 队列只有一个元素 头部置为null
r.next; // Unchecked due to the next field having a raw type in Reference
r.queue = NULL; // 改变引用对象状态
r.next = r;
queueLength--;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(-1);
}
return r;
}
return null;
}
// 从队列移除元素 如果队列内部没有元素 阻塞等待指定时间 超过时间仍没有获取到元素 返回null
public Reference<? extends T> remove(long timeout)
throws IllegalArgumentException, InterruptedException
{
if (timeout < 0) {
throw new IllegalArgumentException("Negative timeout value");
}
synchronized (lock) {
Reference<? extends T> r = reallyPoll();
if (r != null) return r;
long start = (timeout == 0) ? 0 : System.nanoTime();
for (;;) {
lock.wait(timeout);
r = reallyPoll();
if (r != null) return r;
if (timeout != 0) {
long end = System.nanoTime();
timeout -= (end - start) / 1000_000;
if (timeout <= 0) return null;
start = end;
}
}
}
}
ReferenceHandler
Reference类的静态内部类,继承了Thread类。
在Reference静态代码块被实例化,设置为守护线程,优先级别最高,线程名为Reference Handler,实例化之后线程就启动了。
run方法不断循环调用下面的方法,实现将pending队列里面的Reference
实例依次添加到不同的ReferenceQueue
中(取决于Reference里面的queue)。该pending的元素由GC负责加入。
线程的任务可以了理解为不断把等待队列的引用对象加入到其实例化时关联的队列,如果实例化时没有关联队列则不做任何处理。
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 {
// 等待队列没有元素 则再次阻塞
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
// 如果为cleaner类实例 调用clean方法后直接返回
if (c != null) {
c.clean();
return true;
}
ReferenceQueue<? super Object> q = r.queue;
// 如果实例化时关联了队列 则加入到队列中
if (q != ReferenceQueue.NULL) q.enqueue(r);
return true;
}
Finalizer
继承了FinalReference。
主要实现:当一个类重写Object了的finalize方法时,实例化此类对象时,JVM会调用Finalizer的register方法实例化一个Finalizer对象,保存了此对象的引用。
当此对象被GC标记为不可达对象时,持有此对象引用的Fimalizer对象会被加入实例化时关联的ReferenceQueue中,随后FinalizeThread会从队列中取出对象执行其finalize方法。
// Finalizer对象的关联队列
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
// Finalizer链表的头
private static Finalizer unfinalized = null;
// 前驱 后继
private Finalizer next = null,prev = null;
// 判断引用的Object方法 finalize方法是否执行
// 每次执行finalize方法之前都会调用此方法判断是否之前已经执行过
// 保证finalize方法只会执行一次
private boolean hasBeenFinalized() {
return (next == this);
}
// 将此对象采用头插法添加到链表 实例化时调用
private void add() {
synchronized (lock) {
if (unfinalized != null) {
this.next = unfinalized;
unfinalized.prev = this;
}
unfinalized = this;
}
}
// 将此对象从链表移除 执行引用Object finalize方法执行调用
private void remove() {
synchronized (lock) {
if (unfinalized == this) {
if (this.next != null) {
unfinalized = this.next;
} else {
unfinalized = this.prev;
}
}
if (this.next != null) {
this.next.prev = this.prev;
}
if (this.prev != null) {
this.prev.next = this.next;
}
// 将前驱和后继都指向自己 表明引用的Obejc finalize方法已经执行
this.next = this; /* Indicates that this has been finalized */
this.prev = this;
}
}
// 执行引用对象的finalize方法
private void runFinalizer(JavaLangAccess jla) {
synchronized (this) {
// 判断之前是否已经执行过
if (hasBeenFinalized()) return;
// 将Finalize对象从链表移除
remove();
}
try {
// 获取引用对象
Object finalizee = this.get();
if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
// 执行finalize方法
jla.invokeFinalize(finalizee);
/* Clear stack slot containing this variable, to decrease
the chances of false retention with a conservative GC */
finalizee = null;
}
} catch (Throwable x) { }
// 清除对引用对象的引用
super.clear();
}
FinalizerThread
Finalizer的静态内部类型,继承了Thread,在Finalizer静态代码块被实例化,然后启动线程,线程优先级较低,线程名为Finalizer。
主要实现从FinalReference关联的队列中取出引用对象,执行其finalize方法。
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
if (running)
return;
// Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (!VM.isBooted()) {
// delay until VM completes initialization
try {
VM.awaitBooted();
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
// 不断的从关联的队列中取出引用对象 执行其finalize方法
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and continue
}
}
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2); // 设置线程优先级为8
finalizer.setDaemon(true); // 设置为守护线程
finalizer.start(); // 启动线程
}
一个执行finalize方法时对象重新复活的例子
public class TestFinalReference {
static TestObject Object;
public static void main(String[] args) throws InterruptedException {
TestObject testObject = new TestObject();
System.out.println(testObject);
testObject = null;
System.gc();
Thread.sleep(3000);
System.out.println(Object);
}
static class TestObject {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("执行了finalize方法");
Object = this;
}
}
}
-XX:+PrintReferenceGC
打印GC时各个引用类型的数量 以及处理的时间
[GC (Allocation Failure) [SoftReference, 0 refs, 0.0000119 secs][WeakReference, 499 refs, 0.0000370 secs][FinalReference, 1045 refs, 0.0002313 secs][PhantomReference, 0 refs, 0 refs, 0.0000039 secs][JNI Weak Reference, 0.0000290 secs][PSYoungGen: 53456K->4880K(57344K)] 53496K->4928K(253952K), 0.0037199 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
-XX:+ParallelRefProcEnabled
开启并行回收引用对象