本文基于JDK1.8.0_271分析,native源码下载自openJDK官网(build 1.8.0_41-b04)
0. 前言
JDK1.2开始,引入了一个新的包,java.lang.ref
:
- java.lang.ref
- Finalizer.class
- FinalizerHistogram.class
- FinalReference.class
- PhantomReference.class
- Reference.class
- ReferenceQueue.class
- SoftReference.classs
- WeakReference.class
- 额外的还有一个在sun.misc包下
- Cleaner.class
随之带来了四个新的概念:
- 强引用Storn References:随处可见,我们直接new出来的代码就是强引用。内存不足时,宁愿抛出
OutOfMemoryError
也不愿意回收这些对象。我们可以手动的设置为null让GC回收他。 - 软引用SoftReference:等级比强引用低,只有在内存不足的时候才回去回收。我们可以实现内存敏感的高速缓存。
- 弱引用WeakReference:等级比软引用低,不管内存足不足,发生GC时,就有可能被回收。如果这个对象是一个偶尔使用的对象,并且需要在使用的时候就能获取到,但又不想影响生命周期,就可以使用WeakReference,比如处理内部类内存泄漏的问题时。
- 虚引用PhantomReference:等级比弱引用低,形同虚设,任何时候都可能被回收。它可以和引用队列配合用于监控对象是否被回收
并且,除了强引用之外,其他三个都可以和引用队列配合使用,就是在调用他们的构造方法时,除了传入Object外,还可以传入一个ReferenceQueue,他的作用就是当Object被回收时,JVM会自动帮我们把这个软/弱/虚引用添加到这个Queue中。通过这个功能可以监控对象是否被回收。
直到JDK8为止,只存在四种引用,这些引用是由JVM创建,因此直接继承java.lang.ref.Reference
创建自定义的引用类型是无效的,但是可以直接继承已经存在的引用类型,如sun.misc.Cleaner
就是继承自java.lang.ref.PhantomReference
。
接下来我们就可以源码,但是在此之前,我们得带着问题看:
- 软引用是内存不足才会回收,那么什么叫内存不足?
- 弱引用只要发生GC时就回收,但是他不是引用着对应的强引用,那么他为啥能被回收?
- 虚引用形同虚设,任何时候都会被回收,那么到底什么时候会被回收?
- 当引用对象被回收时,他们是怎么被添加到若引用队列的?
1. Reference.java
1.1 源码
首先来看下他的构造方法和成员变量:
public abstract class Reference<T> {
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
@SuppressWarnings("rawtypes")
volatile Reference next;
transient private Reference<T> discovered; /* used by VM */
static private class Lock {
}
private static Lock lock = new Lock();
private static Reference<Object> pending = null;
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
}
1.1.1 构造方法
构造方法有两种,一种只传入referent,另一种还要传入一个Queue,如果传入的queue为null的话,成员变量queue就等于ReferenceQueue.NULL
。
1.1.2 成员变量
-
reference:代表我们传入的对象。
-
queue: ReferenceQueue,就是引用队列。
volatile ReferenceQueue<? super T> queue;
-
next:下一个Reference实例的引用,主要是在ReferenceQueue中使用。
/* When active: NULL * pending: this * Enqueued: next reference in queue (or this if last) * Inactive: this */ @SuppressWarnings("rawtypes") volatile Reference next;
-
discovered:注意这个属性由transient修饰,基于状态表示不同链表中的下一个待处理的对象,主要是PendingList的下一个元素,通过JVM直接调用赋值。
/* 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 */ private transient Reference<T> discovered;
-
lock: 线程安全的锁
/* 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();
-
pending:等待加入queue的Reference对象,在GC时由JVM设置,会有一个java层的线程(ReferenceHandler)源源不断从PendingList中取出元素加入到queue中
/* 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;
1.1.3 方法
/**
* 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
*/
// 获取referent实例
@HotSpotIntrinsicCandidate
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.
*/
// 清除referent实例
public void clear(