我尝试使用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)
方法,那么它就像魅力一样!
任何人都可以解释这个特定的行为吗?我现在已经有很长一段时间一直在寻找答案了。
答案 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();
}
}
}
此测试按预期成功。