为什么ReferenceQueue总是空的?

时间:2014-09-23 14:58:02

标签: java garbage-collection weak-references

我尝试使用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 s,但没有区别。

有趣的是,如果您将单元测试转换为常规public static void main(String[] args)方法,那么它就像魅力一样!

任何人都可以解释这个特定的行为吗?我现在已经有很长一段时间一直在寻找答案了。

2 个答案:

答案 0 :(得分:2)

这对我来说似乎是一种竞争条件。当GC确定myObject1引用的对象是GCable时,它将对其进行GC并从WeakReference中清除它。然后,它会将WeakReference添加到“待处理参考列表”中。有一个引用处理程序线程,它将从该列表中删除并添加到相应的ReferenceQueue。以上是支持javadoc

的实现细节
  

在同一时间或稍晚时间,它会将这些列入其中   新引用的弱引用,通过引用注册   队列。

代码到达时

Reference<? extends Object> removedRef = refQueue.poll();

参考处理程序线程不得将WeakReference添加到ReferenceQueue,因此poll会返回null

答案 1 :(得分:0)

好吧,这可能不是一个明确的答案,但至少从问题中得到单元测试才能正常运行。我尝试使用以下类,而不是常规的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来跟踪
  • 仅适用于WeakReference s,不适用于PhantomReference s(因为他们的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();
        }
    }

}

此测试按预期成功。