Reference JDK有四個子類,分爲WeakReference,SoftReference,PhantomReference,FinalReference。java
弱引用,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()); }
輸出:ide
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.測試
軟引用,JVM沒有內存的時候就開始回收,注意,這裏的回收是指的是JVM的操做,與上面WeakReference相似,只是JVM清理的時機不一樣。this
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()); }
手動入隊的。spa
true 0 true 1 true 2 true 3 true 4 null false null false null false null false null false
幽靈引用,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方法獲取不到.code
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"); } }