强引用没有对应的类型表示,也就是说强引用是普遍存在的,如Object object = new Object();。 软引用、弱引用和虚引用都是java.lang.ref.Reference的直接子类。 直到JDK11为止,只存在四种引用,这些引用是由JVM创建,因此直接继承java.lang.ref.Reference创建自定义的引用类型是无效的,但是可以直接继承已经存在的引用类型,如java.lang.ref.Cleaner就是继承自java.lang.ref.PhantomReference。 特殊的java.lang.ref.Reference的子类java.lang.ref.FinalReference和Object#finalize()有关,java.lang.ref.Finalizer是java.lang.ref.FinalReference子类,下文会详细分析这些内容。 Reference# Reference就是引用,对JVM的垃圾收集活动敏感(当然,强引用可能对垃圾收集活动是不敏感的),Reference的继承关系或者实现是由JDK定制,引用实例是由JVM创建,所以自行继承Reference实现自定义的引用类型是无意义的,但是可以继承已经存在的引用类型,如SoftReference等。Reference类文件的注释也比较简短,但是方法和变量的注释十分详细,特别是用图表表明了状态跃迁的过程,这里先看类文件头注释:
Abstract base class for reference objects. This class defines the operations common to all reference objects. Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.
构造函数依赖于一个泛型的referent成员以及一个ReferenceQueue super T>的队列,如果ReferenceQueue实例为null,则使用ReferenceQueue.NULL。
成员变量描述:
referent:Reference保存的引用指向的对象,下面直接称为referent。 Copy // GC特殊处理的对象 private T referent; /* Treated specially by GC / queue:Reference对象关联的队列,也就是引用队列,对象如果即将被垃圾收集器回收,此队列作为通知的回调队列,也就是当Reference实例持有的对象referent要被回收的时候,Reference实例会被放入引用队列,那么程序执行的时候可以从引用队列得到或者监控相应的Reference实例。 Copy / The queue this reference gets enqueued to by GC notification or by * calling enqueue(). * * When registered: the queue with which this reference is registered. * enqueued: ReferenceQueue.ENQUEUE * dequeued: ReferenceQueue.NULL * unregistered: ReferenceQueue.NULL / volatile ReferenceQueue super T> queue; next:下一个Reference实例的引用,Reference实例通过此构造单向的链表。 Copy / The link in a ReferenceQueue’s list of Reference objects. * * When registered: null * enqueued: next element in queue (or this if last) * dequeued: this (marking FinalReferences as inactive) * unregistered: null / @SuppressWarnings(“rawtypes”) volatile Reference next; discovered:注意这个属性由transient修饰,基于状态表示不同链表中的下一个待处理的对象,主要是pending-reference列表的下一个元素,通过JVM直接调用赋值。 Copy / 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 discovered; / used by VM */ 实例方法(和ReferenceHandler线程不相关的方法):
Copy // 获取持有的referent实例 @HotSpotIntrinsicCandidate public T get() { return this.referent; }
// 把持有的referent实例置为null public void clear() { this.referent = null; }
Copy // ReferenceHandler直接继承于Thread覆盖了run方法 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 {// 确保Cleaner这个类已经初始化// pre-load and initialize Cleaner class so that we don't// get into trouble later in the run loop if there's// memory shortage while loading/initializing it lazily.ensureClassInitialized(Cleaner.class);}ReferenceHandler(ThreadGroup g, String name) {super(g, null, name, 0, false);}// 注意run方法是一个死循环执行processPendingReferencespublic void run() {while (true) {processPendingReferences();}}
}/* 原子获取(后)并且清理VM中的pending引用链表* Atomically get and clear (set to null) the VM's pending-Reference list.*/
private static native Reference
ReferenceHandler线程启动的静态代码块如下:
Copy static { // ThreadGroup继承当前执行线程(一般是主线程)的线程组 ThreadGroup tg = Thread.currentThread().getThreadGroup(); for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent()); // 创建线程实例,命名为Reference Handler,配置最高优先级和后台运行(守护线程),然后启动 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(); // 注意这里覆盖了全局的jdk.internal.misc.JavaLangRefAccess实现 // provide access in SharedSecrets SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { @Override public boolean waitForReferenceProcessing() throws InterruptedException{ return Reference.waitForReferenceProcessing(); }
Copy final class Finalizer extends FinalReference { /* Package-private; must be in same package as the Reference class */ // Finalizer关联的ReferenceQueue,其实Finalizer是一个特殊的Reference实现 private static ReferenceQueue queue = new ReferenceQueue<>();
/** Head of doubly linked list of Finalizers awaiting finalization. */
// 等待finalization的所有Finalizer实例链表的头节点,这里称此链表为unfinalized链表
private static Finalizer unfinalized = null;/** Lock guarding access to unfinalized list. */
// unfinalized链表的锁,静态final,全局的锁实例
private static final Object lock = new Object();// 中间变量,分别记录unfinalized链表中当前执行元素的下一个节点和前一个节点
private Finalizer next, prev;private Finalizer(Object finalizee) {super(finalizee, queue);// push onto unfinalized// 这里主要是更新unfinalized链表的头节点,新增的元素总是会变成头节点synchronized (lock) {if (unfinalized != null) {this.next = unfinalized;unfinalized.prev = this;}unfinalized = this;}
}static ReferenceQueue getQueue() {return queue;
}/* Invoked by VM */ 这个方法由JVM激活,也就是链表的元素入队是由JVM控制的,见下文分析
static void register(Object finalizee) {new Finalizer(finalizee);
}private void runFinalizer(JavaLangAccess jla) {synchronized (lock) {// 当前元素已经处理过,直接返回if (this.next == this) // already finalizedreturn;// unlink from unfinalized// 下面的逻辑是当前需要执行的元素从链表中移除,并且更新prev和next的值,相当于重建链表的部分节点if (unfinalized == this)unfinalized = this.next;elsethis.prev.next = this.next;if (this.next != null)this.next.prev = this.prev;this.prev = null;this.next = this; // mark as finalized}try {// 获取对象执行一次finalize方法Object finalizee = this.get();if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {jla.invokeFinalize(finalizee);// Clear stack slot containing this variable, to decrease// the chances of false retention with a conservative GC// 清空变量引用从而减少保守GC导致变量保留的可能性finalizee = null;}} catch (Throwable x) { }// 执行完毕会做一次情况防止重复执行super.clear();
}/* Create a privileged secondary finalizer thread in the system thread* group for the given Runnable, and wait for it to complete.** This method is used by runFinalization.** It could have been implemented by offloading the work to the* regular finalizer thread and waiting for that thread to finish.* The advantage of creating a fresh thread, however, is that it insulates* invokers of that method from a stalled or deadlocked finalizer thread.*/
// 这里其实不用畏惧注释太多,它只是一个候选方法,新建一个线程直接调用包裹在Runnable的runFinalization方法,主要是提供给主动调用的上层方法调用的
private static void forkSecondaryFinalizer(final Runnable proc) {AccessController.doPrivileged(new PrivilegedAction<>() {public Void run() {ThreadGroup tg = Thread.currentThread().getThreadGroup();for (ThreadGroup tgn = tg;tgn != null;tg = tgn, tgn = tg.getParent());Thread sft = new Thread(tg, proc, "Secondary finalizer", 0, false);sft.start();try {sft.join();} catch (InterruptedException x) {Thread.currentThread().interrupt();}return null;}});
}/* Called by Runtime.runFinalization() */
// 这个方法是给Runtime.runFinalization()委托调用的,其实就是主动取出queue的元素强制调用其finalize方法
static void runFinalization() {if (VM.initLevel() == 0) {return;}forkSecondaryFinalizer(new Runnable() {private volatile boolean running;public void run() {// in case of recursive call to run()if (running)return;final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();running = true;for (Finalizer f; (f = (Finalizer)queue.poll()) != null;)f.runFinalizer(jla);}});
}// 真正的Finalizer线程
private static class FinalizerThread extends Thread {private volatile boolean running;FinalizerThread(ThreadGroup g) {super(g, null, "Finalizer", 0, false);}public void run() {// in case of recursive call to run()if (running)return;// Finalizer thread starts before System.initializeSystemClass// is called. Wait until JavaLangAccess is availablewhile (VM.initLevel() == 0) {// delay until VM completes initializationtry {VM.awaitInitLevel(1);} catch (InterruptedException x) {// ignore and continue}}final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();running = true;// 注意这里是死循环for (;;) {try {// 注意这里是调用`Reference#remove()`的永久阻塞版本,只有`Reference#enqueue()`被调用才会解除阻塞// `Reference#remove()`解除阻塞说明元素已经完成入队,由ReferenceHandler线程完成Finalizer f = (Finalizer)queue.remove();// 实际上就是调用对象的finalize方法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());// 静态代码块中声明线程,优先级是最高优先级-2,守护线程,实际上这里优先级不一定会生效Thread finalizer = new FinalizerThread(tg);finalizer.setPriority(Thread.MAX_PRIORITY - 2);finalizer.setDaemon(true);finalizer.start();
}
Copy instanceOop InstanceKlass::register_finalizer(instanceOop i, TRAPS) { if (TraceFinalizerRegistration) { tty->print(“Registered “); i->print_value_on(tty); tty->print_cr(” (” INTPTR_FORMAT “) as finalizable”, p2i(i)); } instanceHandle h_i(THREAD, i); // Pass the handle as argument, JavaCalls::call expects oop as jobjects JavaValue result(T_VOID); JavaCallArguments args(h_i); // 这里Universe::finalizer_register_method()获取到的就是Finalizer#register方法句柄 methodHandle mh (THREAD, Universe::finalizer_register_method()); JavaCalls::call(&result, mh, &args, CHECK_NULL); return h_i(); } 最后调用的是javaCalls.cpp:
Copy void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) { // Check if we need to wrap a potential OS exception handler around thread // This is used for e.g. Win32 structured exception handlers assert(THREAD->is_Java_thread(), “only JavaThreads can make JavaCalls”); // Need to wrap each and every time, since there might be native code down the // stack that has installed its own exception handlers os::os_exception_wrapper(call_helper, result, method, args, THREAD); } 简单来看就是把创建对象过程中,如果有必要注册Finalizer(一般是覆盖了finalize()方法),则基于当前线程通过Finalizer#register(Object finalizee)把当前新建的实例注册到Finalizer自身维护的链表中(如果没理解错,所谓的F-Queue就是这个链表了),等待后台Finalizer线程轮询并且执行链表中对象的finalize()方法。
StrongReference# StrongReference也就是强引用,它是使用最普遍的一种引用,java.lang.ref包下没有强引用对应的类型。一个比较明确的强引用定义就是:所有和GC Root之间存在引用链的对象都具备强引用。举个简单例子:形如Object o = new Object();在方法体中使用new关键字声明的对象一般就是强引用。如果一个对象具备强引用,垃圾回收器绝不会回收它。当内存空间不足,JVM宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会出现回收具有强引用的对象来解决内存不足的情况。当然,如果有共享的成员变量在方法退出之前置为null,相当于断绝成员变量和GC Root的引用链,在合适的时机是有利于GC后具备强引用的对象的回收,例如:
Copy private Object shareValue = XXX;
public void methodA(){ //do something shareValue = null; } 后来有人过度信奉类似上面的这个实践,出现了一条比较诡异的编码实践:强引用使用完毕后都要置为null方便对象回收。但是实际上,这个实践并不是在任何场景都是合理的。
public static void main(String[] args) throws Exception {try (CleaningExample o = new CleaningExample(11)){}CleaningExample o2 = new CleaningExample(22);System.gc();Thread.sleep(300);
}
}
class CleaningExample implements AutoCloseable {
private Cleaner cleaner = Cleaner.create();
private final State state;
private final Cleaner.Cleanable cleanable;public CleaningExample(int s) {state = new State(s);cleanable = cleaner.register(this, state);
}class State implements Runnable {private final int s;public State(int s) {this.s = s;}@Overridepublic void run() {System.out.println("State runnable in action.State value = " + s);}
}@Override
public void close() throws Exception {cleanable.clean();
}
} 实际上,沙面的代码执行完毕只会输出"State runnable in action.State value = 11",并没有输出"State runnable in action.State value = 22",这是因为无法预测强引用对象被回收的时机。java.lang.ref.Cleaner主要是用于预防实现了AutoCloseable接口的实例忘记调用close()方法在对象被垃圾收集器回收的时候(内存回收)做一个兜底的清理工作,在JDK9之后,java.lang.ref.Cleaner主要是为了替代已经标识为过期的Object#finalize()方法。