建議先看完Java中的各種引用之後 再繼續閱讀本文
開宗明義 Reference Queue就是一個放引用的地方 只要這個引用指到的人(referent)已經被垃圾回收器判斷要被回收 這個引用就會被丟進reference queue
看例子
public class GarbageCollectionExample {
public static void main(String[] args) throws Exception{
JYTClass strongReference = new JYTClass("Demo");
ReferenceQueue<JYTClass> referenceQueue = new ReferenceQueue<JYTClass>();
WeakReference<JYTClass> weakReference = new WeakReference<JYTClass>(strongReference, referenceQueue);
System.out.println("Now to call gc...");
Runtime.getRuntime().gc();
Thread.sleep(1000);
System.out.println("Any weak references in Queue? " + (referenceQueue.poll() != null));
strongReference = null;
System.out.println("Now to call gc...");
Runtime.getRuntime().gc();
Thread.sleep(1000);
System.out.println("Is the weak reference added to the ReferenceQ ? " + (weakReference.isEnqueued()));
System.out.println("Does the weak reference still hold the heap object ? " + (weakReference.get() != null));
Reference<? extends JYTClass> weakRefInQueue = referenceQueue.remove();
System.out.println("Is this same as original weak reference ? " + (weakRefInQueue == weakReference));
System.out.println("Is the weak reference added to the ReferenceQ ? " + (weakReference.isEnqueued()));
}
}
來解釋一下這段有趣的程式碼
JYTClass strongReference = new JYTClass("Demo");
ReferenceQueue<JYTClass> referenceQueue = new ReferenceQueue<JYTClass>();
WeakReference<JYTClass> weakReference = new WeakReference<JYTClass>(strongReference, referenceQueue);
第一段 先宣告一個強引用跟弱引用 和一個reference queue 當然reference queue裡面要放什麼型態的引用都已經定義好 就是只能放 指到JYTClass的 軟引用弱引用跟虛引用
System.out.println("Now to call gc...");
Runtime.getRuntime().gc();
Thread.sleep(1000);
System.out.println("Any weak references in Queue? " + (referenceQueue.poll() != null));
第一次清理 不會有人進queue 因為還有個強引用指著
接下來把強引用拿掉 呼叫垃圾收集器
strongReference = null;
System.out.println("Now to call gc...");
Runtime.getRuntime().gc();
Thread.sleep(1000);
接下來就是介紹reference queue怎麼用了
System.out.println("Is the weak reference added to the ReferenceQ? " + (weakReference.isEnqueued()));
weakReference.isEnqueued() 回傳true 因為weakReference在reference queue裡 我們可以用這個方法 三不五時地去看看這傢伙指到的人被回收了沒
System.out.println("Does the weak reference still hold the heap object? " + (weakReference.get() != null));
weakReference.get() 因為東西已經被回收 弱引用已經指不到東西了
Reference<? extends JYTClass> weakRefInQueue = referenceQueue.remove();
remove會把referenceQueue裡面的東西移除並回傳 你就可以拿到在referenceQueue裡的引用
注意 remove是blocking call 如果你沒有先確認queue裡面有東西就呼叫remove 你的thread會等到queue裡面有東西為止
System.out.println("Is this same as original weak reference ? " + (weakRefInQueue == weakReference));
weakRefInQueue == weakReference 回傳true 代表說我們宣告的弱引用就是被丟進referenceQueue的那個
System.out.println("Is the weak reference added to the ReferenceQ ? " + (weakReference.isEnqueued()));
weakReference.isEnqueued() 現在回傳false 因為我們把它從queue裡拿掉了
跑出來的結果是這樣
Now to call gc...
Any weak references in Queue? false
Now to call gc...
Is the weak reference added to the ReferenceQ ? true
Does the weak reference still hold the heap object ? false
Is this same as original weak reference ? true
Is the weak reference added to the ReferenceQ ? false
問題來了 那如果我又再finalize()裡面胡搞 救人 weakReference還存取的到物件嗎
public class GarbageCollectionExample1 {
private static JYTClass j;
public static void main(String[] args) throws Exception{
JYTClass strongReference = new JYTClass("Demo");
System.out.println("original reference?" + strongReference);
ReferenceQueue<JYTClass> referenceQueue = new ReferenceQueue<JYTClass>();
WeakReference<JYTClass> weakReference = new WeakReference<JYTClass>(strongReference, referenceQueue);
strongReference = null;
System.out.println("Now to call gc...");
Runtime.getRuntime().gc();
Thread.sleep(1000);
System.out.println("reattached? " + j);
System.out.println("Does the weak reference still hold the heap object ? " + (weakReference.get() != null));
}
static class JYTClass {
String a;
JYTClass(String input) {
a = input;
}
@Override
public void finalize() {
System.out.println("Finalizer of JYTClass");
j = this;
}
}
}
輸出結果
original reference?com.jvm0532.reference.GarbageCollectionExample1$JYTClass@5305068a
Now to call gc...
Finalizer of JYTClass
reattached? com.jvm0532.reference.GarbageCollectionExample1$JYTClass@5305068a
Does the weak reference still hold the heap object ? false
完美 基本上只要Reference進了queue之後 就再也無法從reference存取那個物件了 你要救人我阻止不了你 畢竟finalizer的問題很多大家都知道 但你用弱引用或是虛引用跟ReferenceQ來清理資源 就不會有這種問題
不要用finalize()方法清理釋放你的資源 會有太多無法預期的行為 你可以使用PhantomReference + ReferenceQueue 這樣程式不但沒有任何搞怪(隨便救人)的機會 你還可以決定你想在什麼時候釋放資源(畢竟finalize的優先度很低 永遠不知道JVM什麼時候有空幫你跑)
常見的做法是再開一個thread 跑一個while迴圈去呼叫referenceQueue.poll 一有東西就可以開始釋放資源