簡體   English   中英

為什么ReferenceQueue始終為空?

[英]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.

......這是很好的,但測試總是失敗,由於trackedRemovalfalse的結束-的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;
    }
}

這種方法的缺點是:

  • 需要明確enqueueWeakReference 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM