Reference源码
/**
* 引用对象(reference objects)的抽象基类。
* 此类定义了所有引用对象的抽象操作。
* 因为这个类是在与garbage collector 紧密合作的情况下实现的,所以此类不能被子类化(就是 我们无法自定义一个类用于继承这个类)。
*
*/
public abstract class Reference<T> {
/* A reference instance 处于四种可能的内部状态之一:
* Active(活动状态):
* 一个引用对象,并且状态是Active,就会被garbage collector 特殊对待.
* 垃圾收集器在检测到 引用对象的可到达性 已经被更改为适当的状态后(实际对象处于 GC roots 不可达),
* 会根据实例在创建时是否向队列注册了实例,将实例的状态更改为“Pending” 或者 “Inactive”。
*
* 如果 Reference 注册了ReferenceQueue(通俗的说,就是new Reference时,传入了一个ReferenceQueue参数),则会切换为Pending,并且Reference会加入pending-Reference链表中,
* 如果没有注册ReferenceQueue,会切换为Inactive
*
* 新创建的实例处于活动状态。
*
* Pending(挂起状态):
* 等待被Reference-handler线程加入ReferenceQueue队列的 pending-reference链表中的 元素,都是处于此状态
*
* Enqueued:
* ReferenceQueue队列中的Reference的状态
* 当一个实例从其ReferenceQueue中删除时,它就变成Inactive的。未注册的实例永远不会处于此状态(就是new时,没有传入ReferenceQueue参数的实例 永远不会处于此状态)。
*
* Inactive(不活动状态):
* 不做任何操作。一旦实例处于此状态,那么他的状态再也不会发生改变。
* 同时说明该引用实例所指向的实际对象一定会被GC所回收
*
* 每个状态的队列 以及 next对象如下所示
*
* Active:
* queue = ReferenceQueue with which instance is registered, or ReferenceQueue.NULL if it was not registered with a queue;
* next = null.
*
* Pending:
* queue = ReferenceQueue with which instance is registered;
* next = this
*
* Enqueued:
* queue = ReferenceQueue.ENQUEUED;
* next = Following instance in queue, or this if at end of list.
*
* Inactive: queue = ReferenceQueue.NULL; next = this.
*
* 通过使用这种方式,垃圾收集器 只需要检查 next字段就能决定,这个引用实例是否需要特殊处理。
* 如果next字段是null,那么这个实例处于Active状态
* 如果next字段非null,收集器正常处理这个实例
*
*/
/**
* 要搞清楚,referent 和 Reference 的区别。referent是目标对象,Reference只是对referent的“包装”。
* referent被Reference“包装后”,就会被Garbage Collector特殊处理。
* 特殊之处在于:gc时,Garbage Collector 会 “优先” 回收 referent对象,要求更严格一点。
* 软引用:Garbage Collector 会在堆内存不够时,回收referent对象(即使Gc Root可达referent对象)
* 弱引用:Garbage Collector 每次gc,都会回收referent对象(即使Gc Root可达referent对象)
* 而普通的引用,无论内存够不够,只要Gc Root可达,都不会回收
*
*/
private T referent; /* Treated specially by GC */
/**
* referent对象被回收后,此时referent=null,但是 Reference!=null。
* 如果new Reference时,传入ReferenceQueue参数,referent被回收后,当前Reference实例会被添加到这个队列中。Reference加入到ReferenceQueue后, Reference的queue就会被设置为ReferenceQueue.ENQUEUED,来标识当前Reference已经进入到队里里面了;
* 如果没有传入ReferenceQueue参数,Reference会直接被回收掉
*
* 实际上ReferenceQueue 只有一个Reference head,通过 head.next -> head.next.next -> head.next.next.next 形成一个queue
*
*/
volatile ReferenceQueue<? super T> queue;
/* When active: NULL
* pending: this
* Enqueued: next reference in queue (or this if last)
* Inactive: this
*/
//当前Reference实例的“下一个”,ReferenceQueue基于此字段实现的
@SuppressWarnings("rawtypes")
volatile java.lang.ref.Reference next;
/**
* 整个jvm中唯一,与discovered结合使用,pending 与 discovered构成了一个链表,这个链表就是pending-Reference链表,链表中存放了 需要加入ReferenceQueue队列的元素
*
* Garbage Collector 回收了referent对象后,会把相应的Reference对象放入pending-Reference链表中
*
* pending-Reference链表: pending -> pending.discovered -> pending.discovered.discovered -> pending.discovered.discovered.discovered
* Reference-handler线程 把 pending 入队到 ReferenceQueue,入队后 r = pending; pending = r.discovered; r.discovered = null;
*
* 进入pending-Reference链表,Reference对象需要满足两个条件
* 1. Reference所引用的对象已经不存在其它强引用;
* 2. Reference对象在创建的时候,指定了ReferenceQueue;
*/
private static java.lang.ref.Reference<Object> pending = null;
//由transient修饰,基于状态表示不同链表中的下一个待处理的对象,主要是pending-reference链表的下一个元素,通过JVM直接调用赋值
transient private Reference<T> discovered; /* used by VM */
/**
* Reference-handler线程需要同步操作,Lock用于提供 synchronized关键字所需要的锁对象
*/
static private class Lock {
}
private static Lock lock = new Lock();
/* High-priority thread to enqueue pending References
*/
/**
* 这就是Reference-handler线程了,作用就是 把pending-Reference链表中的Reference 视情况加入到 ReferenceQueue 或者 丢弃
*/
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,用于将pending-Reference链表的元素加入到ReferenceQueue
tryHandlePending(true);
}
}
}
/**
* Try handle pending {@link java.lang.ref.Reference} if there is one.<p>
* Return {@code true} as a hint that there might be another
* {@link java.lang.ref.Reference} pending or {@code false} when there are no more pending
* {@link java.lang.ref.Reference}s at the moment and the program can do some other
* useful work instead of looping.
*
* @param waitForNotify if {@code true} and there was no pending
* {@link java.lang.ref.Reference}, wait until notified from VM
* or interrupted; if {@code false}, return immediately
* when there is no pending {@link java.lang.ref.Reference}.
* @return {@code true} if there was a {@link java.lang.ref.Reference} pending and it
* was processed, or we waited for notification and either got it
* or thread was interrupted before being notified;
* {@code false} otherwise.
*/
static boolean tryHandlePending(boolean waitForNotify) {
java.lang.ref.Reference<Object> r;
Cleaner c;
try {
synchronized (lock) {
// 此处,进行同步操作,因为不仅ReferenceHandler线程会操作 pending-Reference链表(删除),gc线程也会操作pending-Reference链表(添加)
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;
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new java.lang.ref.Reference.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);
}
});
}
/* -- Referent accessor and setters -- */
/**
* Returns this reference object's referent. If this reference object has
* been cleared, either by the program or by the garbage collector, then
* this method returns <code>null</code>.
*
* @return The object to which this reference refers, or
* <code>null</code> if this reference object has been cleared
*/
public T get() {
return this.referent;
}
/**
* Clears this reference object. Invoking this method will not cause this
* object to be enqueued.
*
* <p> This method is invoked only by Java code; when the garbage collector
* clears references it does so directly, without invoking this method.
*/
public void clear() {
this.referent = null;
}
/* -- Queue operations -- */
/**
* Tells whether or not this reference object has been enqueued, either by
* the program or by the garbage collector. If this reference object was
* not registered with a queue when it was created, then this method will
* always return <code>false</code>.
*
* @return <code>true</code> if and only if this reference object has
* been enqueued
*/
public boolean isEnqueued() {
return (this.queue == ReferenceQueue.ENQUEUED);
}
/**
* Adds this reference object to the queue with which it is registered,
* if any.
*
* <p> This method is invoked only by Java code; when the garbage collector
* enqueues references it does so directly, without invoking this method.
*
* @return <code>true</code> if this reference object was successfully
* enqueued; <code>false</code> if it was already enqueued or if
* it was not registered with a queue when it was created
*/
public boolean enqueue() {
return this.queue.enqueue(this);
}
/* -- Constructors -- */
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}
Reference的状态变化如下图所示:
总结:把一个普通的引用变为一个特殊的引用,Garbage Collector特殊处理这个引用,并且在回收这个引用后,把这个引用的
ReferenceQueue源码
ReferenceQueue源码 就不一一解读了,这个很简单,主要关注enqueue方法
public class ReferenceQueue<T> {
/**
* Constructs a new reference-object queue.
*/
public ReferenceQueue() {
}
private static class Null<S> extends ReferenceQueue<S> {
boolean enqueue(Reference<? extends S> r) {
return false;
}
}
static ReferenceQueue<Object> NULL = new Null<>();
static ReferenceQueue<Object> ENQUEUED = new Null<>();
static private class Lock {
};
private Lock lock = new Lock();
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.notifyAll();
return true;
}
}
private Reference<? extends T> reallyPoll() {
/* Must hold lock */
Reference<? extends T> r = head;
if (r != null) {
@SuppressWarnings("unchecked")
Reference<? extends T> rn = r.next;
head = (rn == r) ? null : rn;
r.queue = NULL;
r.next = r;
queueLength--;
if (r instanceof FinalReference) {
sun.misc.VM.addFinalRefCount(-1);
}
return r;
}
return null;
}
/**
* Polls this queue to see if a reference object is available. If one is
* available without further delay then it is removed from the queue and
* returned. Otherwise this method immediately returns <tt>null</tt>.
*
* @return A reference object, if one was immediately available,
* otherwise <code>null</code>
*/
public Reference<? extends T> poll() {
if (head == null)
return null;
synchronized (lock) {
return reallyPoll();
}
}
/**
* Removes the next reference object in this queue, blocking until either
* one becomes available or the given timeout period expires.
*
* <p> This method does not offer real-time guarantees: It schedules the
* timeout as if by invoking the {@link Object#wait(long)} method.
*
* @param timeout If positive, block for up to <code>timeout</code>
* milliseconds while waiting for a reference to be
* added to this queue. If zero, block indefinitely.
*
* @return A reference object, if one was available within the specified
* timeout period, otherwise <code>null</code>
*
* @throws IllegalArgumentException
* If the value of the timeout argument is negative
*
* @throws InterruptedException
* If the timeout wait is interrupted
*/
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;
}
}
}
}
/**
* Removes the next reference object in this queue, blocking until one
* becomes available.
*
* @return A reference object, blocking until one becomes available
* @throws InterruptedException If the wait is interrupted
*/
public Reference<? extends T> remove() throws InterruptedException {
return remove(0);
}
/**
* Iterate queue and invoke given action with each Reference.
* Suitable for diagnostic purposes.
* WARNING: any use of this method should make sure to not
* retain the referents of iterated references (in case of
* FinalReference(s)) so that their life is not prolonged more
* than necessary.
*/
void forEach(Consumer<? super Reference<? extends T>> action) {
for (Reference<? extends T> r = head; r != null;) {
action.accept(r);
@SuppressWarnings("unchecked")
Reference<? extends T> rn = r.next;
if (rn == r) {
if (r.queue == ENQUEUED) {
// still enqueued -> we reached end of chain
r = null;
} else {
// already dequeued: r.queue == NULL; ->
// restart from head when overtaken by queue poller(s)
r = head;
}
} else {
// next in chain
r = rn;
}
}
}
}
Reference 实例演示
public class Apple {
private String name;
public Apple(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 覆盖finalize,在回收的时候会执行。
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Apple: " + name + " finalize。");
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + '\'' +
'}' + ", hashCode:" + this.hashCode();
}
}
public class WeakReferenceTest2 {
public static void main(String[] args) {
ReferenceQueue<Apple> appleReferenceQueue = new ReferenceQueue<>();
WeakReference<Apple> appleWeakReference1 = new WeakReference<Apple>(new Apple("青苹果"), appleReferenceQueue);
WeakReference<Apple> appleWeakReference2 = new WeakReference<Apple>(new Apple("毒苹果"), appleReferenceQueue);
WeakReference<Apple> appleWeakReference3 = new WeakReference<Apple>(new Apple("红苹果"), appleReferenceQueue);
WeakReference<Apple> appleWeakReference4 = new WeakReference<Apple>(new Apple("黄苹果"), appleReferenceQueue);
System.out.println("=====gc调用前=====");
Reference<? extends Apple> reference = null;
while ((reference = appleReferenceQueue.poll()) != null ) {
//不会输出,因为没有回收被弱引用的对象,并不会加入队列中
System.out.println(reference);
}
System.out.println(appleWeakReference1);
System.out.println(appleWeakReference2);
System.out.println(appleWeakReference3);
System.out.println(appleWeakReference4);
System.out.println(appleWeakReference1.get());
System.out.println(appleWeakReference2.get());
System.out.println(appleWeakReference3.get());
System.out.println(appleWeakReference4.get());
System.out.println("=====调用gc=====");
System.gc();
try {
Thread.sleep(5000*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=====gc调用后=====");
//下面两个输出为null,表示对象被回收了
System.out.println(appleWeakReference1.get());
System.out.println(appleWeakReference2.get());
System.out.println(appleWeakReference3.get());
System.out.println(appleWeakReference4.get());
//输出结果,并且就是上面的appleWeakReference、appleWeakReference2,再次证明对象被回收了
Reference<? extends Apple> reference2 = null;
while ((reference2 = appleReferenceQueue.poll()) != null ) {
//如果使用继承的方式就可以包含其他信息了
System.out.println("appleReferenceQueue中:" + reference2);
}
}
}
首先我们看下未调用gc前,各个Reference的状态:
调用System.gc(),触发gc,4个Reference的referent对象都会被回收掉,所以referent都为null。如下图所示
因为在构造4个Reference时,传入了ReferenceQueue,如下图
所以在referent被回收后,appleWeakReference1、appleWeakReference2、 appleWeakReference3、 appleWeakReference4的状态变为pending,并且这4个Reference全部加入到pending-Reference链表(加入这一步骤由gc线程完成)。我们看下pending-Reference链表,如下图:
接下来,我们看下,Reference-handler线程如何将pending-Reference链表中的元素加入到ReferenceQueue,如下图:
此时,就完成了appleWeakReference4(WeakReference@624)的入队操作。我们来看下,此时appleWeakReference4以及ReferenceQueue的状态,如下图
至此为止,appleWeakReference4处理成功。接下来再看下appleWeakReference3(WeakReference@623)的处理,如下图
appleWeakReference3处理完成后,我们看下,appleWeakReference3、appleWeakReference4以及ReferenceQueue的状态,如下图:
接下来appleWeakReference2,以及appleWeakReference1都是重复同样的操作,这里就不重复列举。
最终,这4个Reference全部都会加入到ReferenceQueue,也就是我们之前传入的appleReferenceQueue。我们通过appleReferenceQueue的head(Reference类型),以及head的next,得到4个Reference。
此处,贴下程序的执行结果:
=====gc调用前=====
java.lang.ref.WeakReference@61443d8f
java.lang.ref.WeakReference@445b84c0
java.lang.ref.WeakReference@61a52fbd
java.lang.ref.WeakReference@233c0b17
Apple{
name='青苹果'}, hashCode:1674896058
Apple{
name='毒苹果'}, hashCode:2075203460
Apple{
name='红苹果'}, hashCode:866191240
Apple{
name='黄苹果'}, hashCode:1879492184
=====调用gc=====
Apple: 红苹果 finalize。
Apple: 黄苹果 finalize。
Apple: 毒苹果 finalize。
Apple: 青苹果 finalize。
=====gc调用后=====
null
null
null
null
appleReferenceQueue中:java.lang.ref.WeakReference@233c0b17
appleReferenceQueue中:java.lang.ref.WeakReference@61443d8f
appleReferenceQueue中:java.lang.ref.WeakReference@445b84c0
appleReferenceQueue中:java.lang.ref.WeakReference@61a52fbd
总结
一个对象被某个Reference持有后,会被Garbage Collector 特殊对待。当这个对象被回收后,视情况对这个对象对应的Reference做处理:
- 如果构建Reference时,没有传入ReferenceQueue类型参数,那么不做任何操作;
- 如果传入了ReferenceQueue类型参数,那么把这个对象对应的Referenc加入到 pending-Reference链表,然后再把pending-Reference链表中元素加入到传入的ReferenceQueue中;
关于Reference的四种类型,使用实例,以及各自的用途,会在下一篇博客中讲到。
参考与感谢
关于Java中的WeakReference
阿里面试: 说说强引用、软引用、弱引用、虚引用吧
一提到Reference 百分之九十九的java程序员都懵逼了