Reference JDK有四个子类,分为WeakReference,SoftReference,PhantomReference,FinalReference。
WeakReference
弱引用,JVM扫描到就开始回收里面的T,注意,这里的回收是指的是JVM的操作,对于已经入过队WeakReference JVM不会处理。
ReferenceQueue<String> queue = new ReferenceQueue<>(); LinkedList<WeakReference<String>> weakList = new LinkedList<WeakReference<String>>(); for (int i = 0; i < 5; i++) { WeakReference<String> s = new WeakReference<String>(i + "", queue); weakList.add(s); // s.enqueue(); System.out.println(s.isEnqueued()+" "+s.get()); // s.clear(); } System.gc(); try { // 下面休息几分钟,让上面的垃圾回收线程运行完成 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } Reference<? extends String> s = null; while ((s = queue.poll()) != null) { System.out.println(s.get()+" "+s.isEnqueued()); }
输出:
true 0 true 1 true 2 true 3 true 4 null false null false null false null false null false
下面这段,自己手工入队
ReferenceQueue<String> queue = new ReferenceQueue<>(); LinkedList<WeakReference<String>> weakList = new LinkedList<WeakReference<String>>(); for (int i = 0; i < 5; i++) { WeakReference<String> s = new WeakReference<String>(i + "", queue); weakList.add(s); s.enqueue(); System.out.println(s.isEnqueued()+" "+s.get()); // s.clear(); } System.gc(); try { // 下面休息几分钟,让上面的垃圾回收线程运行完成 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } Reference<? extends String> s = null; while ((s = queue.poll()) != null) { System.out.println(s.get()+" "+s.isEnqueued()); }
true 0 true 1 true 2 true 3 true 4 4 false 3 false 2 false 1 false 0 false
T并没有回收,还在哪里,因为手动入队之后,没有执行clear.
SoftReference
软引用,JVM没有内存的时候就开始回收,注意,这里的回收是指的是JVM的操作,与上面WeakReference类似,只是JVM清理的时机不同。
ReferenceQueue<String> queue = new ReferenceQueue<>(); LinkedList<SoftReference<String>> weakList = new LinkedList<SoftReference<String>>(); for (int i = 0; i < 5; i++) { SoftReference<String> s = new SoftReference<String>(i + "", queue); weakList.add(s); s.enqueue(); System.out.println(s.isEnqueued()+" "+s.get()); s.clear(); } System.gc(); try { // 下面休息几分钟,让上面的垃圾回收线程运行完成 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } Reference<? extends String> s = null; while ((s = queue.poll()) != null) { System.out.println(s.get()+" "+s.isEnqueued()); }
手动入队的。
true 0 true 1 true 2 true 3 true 4 null false null false null false null false null false
PhantomReference
幽灵引用,get()永远返回null,但是构造函数的T会被保存,并且在JVM对幽灵引用入队之后,T还存在.
ReferenceQueue<String> queue = new ReferenceQueue<>(); LinkedList<PhantomReference<String>> weakList = new LinkedList<PhantomReference<String>>(); for (int i = 0; i < 5; i++) { PhantomReference<String> s = new PhantomReference<String>(i + "", queue); weakList.add(s); System.out.println(s.isEnqueued()+" "+s.get()); } System.gc(); try { // 下面休息几分钟,让上面的垃圾回收线程运行完成 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } Reference<? extends String> s = null; while ((s = queue.poll()) != null) { System.out.println(s.get()+" "+s.isEnqueued()); }
false null false null false null false null false null null false null false null false null false null false
注意,reference没有入队,这个在Java8里面是,JVM不保证会立即入队,原文是(at that time or at some later time it will enqueue),Java9变成了立即入队。T通过get方法获取不到.
FinalReference
FinalReference的有个子类是Finalizer,如果某个类实现类Object类的finalize()方法,并不为空,那么在对象生成的时候,会被包装成Finalizer,这个操作由JVM来实现,对程序员透明。
private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); private static Finalizer unfinalized = null; private static final Object lock = new Object(); private Finalizer next = null, prev = null;
注意上面Finalizer的源码,存在一个queue和unfinalized.
queue的作用和上面三个Reference一样,用于实现通知,表示对象要被回收了(JVM)操作。
unfinalized 用于保持强引用,防止当前的Finalizer 对象被回收。
在代码加载的时候,会初始化一个 FinalizerThread 线程来执行 runFinalizer 方法
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 { 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); finalizer.setDaemon(true); finalizer.start(); }
注意,这个线程是一个守护者线程,在后台默默的执行finalize()。不会阻止JVM关闭。
注意这个方法。
private void runFinalizer(JavaLangAccess jla) { synchronized (this) { if (hasBeenFinalized()) return; remove(); } try { 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 */ finalizee = null; } } catch (Throwable x) { } super.clear(); }
如果已经执行过finalize(),移除Finalizer对象,要不然就移除当前的 Finalizer,要在第二次GC的时候才会回收Finalizer。
当虚拟机要关闭的时候的finalize()调用过程。
/* Invoked by java.lang.Shutdown */ static void runAllFinalizers() { if (!VM.isBooted()) { return; } forkSecondaryFinalizer(new Runnable() { private volatile boolean running; public void run() { if (running) return; final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); running = true; for (;;) { Finalizer f; synchronized (lock) { f = unfinalized; if (f == null) break; unfinalized = f.next; } f.runFinalizer(jla); }}}); }
fork-join的搞法。开启一个线程去执行finalize()
private static void forkSecondaryFinalizer(final Runnable proc) { AccessController.doPrivileged( new PrivilegedAction<Void>() { 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"); sft.start(); try { sft.join(); } catch (InterruptedException x) { /* Ignore */ } return null; }}); }
另外一种调用RunAllFinalizers()方法的方法,后面的逻辑和shutdown调用一致。这个方法不推荐被使用,可能会在存活的对象上调用,这样才是正确做
Runtime.getRuntime().runFinalization();
/* Called by Runtime.runFinalization() */ static void runFinalization() { if (!VM.isBooted()) { return; } forkSecondaryFinalizer(new Runnable() { private volatile boolean running; public void run() { if (running) return; final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); running = true; for (;;) { Finalizer f = (Finalizer)queue.poll(); if (f == null) break; f.runFinalizer(jla); } } }); }
测试代码
public static void main(String[] args) throws InterruptedException { for(int i=0;i< 2;i++) { Test t = new Test(); } Runtime.getRuntime().runFinalization(); // Thread.sleep(6000); } private static class Test { public void print() {System.out.println("t");} @Override protected void finalize() throws Throwable { System.out.println("1231"); } }