[英]Why is ReferenceQueue always empty?
我正在嘗試使用ReferenceQueue釋放垃圾收集對象使用的資源。 問題是,即使有證據證明被引用的對象之一已被垃圾回收,我的參考隊列也總是空的。 這是一個非常簡單,自包含的JUnit測試,它說明了我正在嘗試做的事情(跟蹤對象的移除):
@Test
public void weakReferenceTest() {
ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
Object myObject1 = new Object();
Object myObject2 = new Object();
WeakReference<Object> ref1 = new WeakReference<Object>(myObject1, refQueue);
WeakReference<Object> ref2 = new WeakReference<Object>(myObject2, refQueue);
myObject1 = null;
// simulate the application running and calling GC at some point
System.gc();
myObject1 = ref1.get();
myObject2 = ref2.get();
if (myObject1 != null) {
System.out.println("Weak Reference to MyObject1 is still valid.");
fail();
} else {
System.out.println("Weak Reference to MyObject1 has disappeared.");
}
if (myObject2 != null) {
System.out.println("Weak Reference to MyObject2 is still valid.");
} else {
System.out.println("Weak Reference to MyObject2 has disappeared.");
fail();
}
Reference<? extends Object> removedRef = refQueue.poll();
boolean trackedRemoval = false;
while (removedRef != null) {
if (removedRef == ref1) {
System.out.println("Reference Queue reported deletion of MyObject1.");
trackedRemoval = true;
} else if (removedRef == ref2) {
System.out.println("Reference Queue reported deletion of MyObject2.");
fail();
}
removedRef = refQueue.poll();
}
if (trackedRemoval == false) {
fail();
}
}
對我來說,這總是打印:
Weak Reference to MyObject1 has disappeared.
Weak Reference to MyObject2 is still valid.
......這是很好的,但測試總是失敗,由於trackedRemoval
是false
的結束-的ReferenceQueue
總是空空的。
我使用ReferenceQueue
和/或WeakReference
錯誤? 我也嘗試了PhantomReference
,但是沒有區別。
有趣的是,如果將單元測試轉換為常規的public static void main(String[] args)
方法,則它的工作原理就像一個魅力!
誰能解釋這個特定行為? 我一直在尋找答案已經有一段時間了。
對我來說,這似乎是一種比賽條件。 當GC確定myObject1
引用的對象是GCable時,它將對其進行GC並從WeakReference
清除它。 然后,它將WeakReference
添加到“待處理的引用列表”中。 有一個引用處理程序線程將從該列表中刪除並添加到適當的ReferenceQueue
。 以上是支持javadoc的實現細節
同時或在以后的某個時間,它將排隊那些已在參考隊列中注冊的新清除的弱引用。
當代碼到達時
Reference<? extends Object> removedRef = refQueue.poll();
引用處理程序線程必須未將WeakReference
添加到ReferenceQueue
,因此poll
返回null
。
好的,這可能不是一個明確的答案,但至少可以從問題中進行單元測試以使其正常運行。 我嘗試使用以下類代替常規的java.lang.ref.ReferenceQueue
:
public class MyReferenceQueue<T> {
private Set<WeakReference<T>> WeakReferences = new HashSet<WeakReference<T>>();
public void enqueue(final WeakReference<T> reference) {
if (reference == null) {
throw new IllegalArgumentException("Cannot add reference NULL!");
}
this.WeakReferences.add(reference);
}
public WeakReference<T> poll() {
Iterator<WeakReference<T>> iterator = this.WeakReferences.iterator();
while (iterator.hasNext()) {
WeakReference<T> ref = iterator.next();
T object = ref.get();
if (object == null) {
iterator.remove();
return ref;
}
}
return null;
}
}
這種方法的缺點是:
enqueue
的WeakReference
s到追蹤 WeakReference
,不適用於PhantomReference
(因為它們的get
方法始終按定義返回null
) 這樣,單元測試將按預期成功。 另外,它不依賴GC來做任何特別的事情(例如向某個隊列添加WeakReference
),只是它使WeakReference
無效(它總是可靠地執行)。 也許它做了一件我不知道的丑陋的事情,但是現在它已經完成了工作。
編輯
這是利用我的自定義實現的更新后的JUnit-Test。
public class WeakReferenceTest {
@Test
public void weakReferenceTest() {
MyReferenceQueue<Object> refQueue = new MyReferenceQueue<Object>();
Object myObject1 = new Object();
Object myObject2 = new Object();
// note that the second constructor argument (the reference queue) is gone...
WeakReference<Object> ref1 = new WeakReference<Object>(myObject1);
WeakReference<Object> ref2 = new WeakReference<Object>(myObject2);
// ... instead we enqueue the references manually now
refQueue.enqueue(ref1);
refQueue.enqueue(ref2);
// the rest of the test remains the same
myObject1 = null;
// simulate the application running and calling GC at some point
System.gc();
myObject1 = ref1.get();
myObject2 = ref2.get();
if (myObject1 != null) {
System.out.println("Weak Reference to MyObject1 is still valid.");
fail();
} else {
System.out.println("Weak Reference to MyObject1 has disappeared.");
}
if (myObject2 != null) {
System.out.println("Weak Reference to MyObject2 is still valid.");
} else {
System.out.println("Weak Reference to MyObject2 has disappeared.");
fail();
}
Reference<? extends Object> removedRef = refQueue.poll();
boolean trackedRemoval = false;
while (removedRef != null) {
if (removedRef == ref1) {
System.out.println("Reference Queue reported deletion of MyObject1.");
trackedRemoval = true;
} else if (removedRef == ref2) {
System.out.println("Reference Queue reported deletion of MyObject2.");
fail();
}
removedRef = refQueue.poll();
}
if (trackedRemoval == false) {
fail();
}
}
}
此測試按預期成功。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.