我调查WeakHashMap源代码以获得有关WeakReference
我发现条目看起来像这样:
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
...
因此,当我们创建新条目时,我们会调用super(key, queue);
。它是WeakReference
构造函数。据我所知,在对象将被GC收集后,新的引用(我相信它应该是key
上的引用)将出现在队列中。
此外,我注意到每个操作都会调用的方法:
/**
* Expunges stale entries from the table.
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
看起来我们从队列中获取(Entry<K,V>)
。我不知道如何解释这个(第一个问题)。
这段代码:
public static void main(String[] args) throws InterruptedException {
StringBuilder AAA = new StringBuilder();
ReferenceQueue queue = new ReferenceQueue();
WeakReference weakRef = new WeakReference(AAA, queue);
AAA = null;
System.gc();
Reference removedReference = queue.remove();
System.out.println(removedReference.get());
}
总是输出null,因为GC已经收集了对象
对我而言,奇怪的是我们可以参考已经由GC收集的Object。实际上我希望引用应该出现在队列中,但我无法读取有意义的内容,因为已经收集了对象(第二个问题)。
答案 0 :(得分:0)
看起来我们从队列中获取(Entry)。我不知道如何解释这个
queue.poll()
为您提供通过引用构造函数放入队列的引用实例。在这种情况下,它是Entry<K,V> extends WeakReference<Object>
。
实际上我希望引用应该出现在队列中,但我无法读取有意义的内容,因为已经收集了对象
您可以通过子类化或将其与其他数据相关联来获取Reference
对象本身,您可以使用该对象进行一些清理。通过辅助Map
。您可以通过get
在活着时获得的裁判不相关,Reference
对象本身就是。
答案 1 :(得分:0)
队列返回之前创建的引用对象。所以使用您的示例代码,执行
之后Reference removedReference = queue.remove();
表达式removedReference == weakRef
将评估为true
,因为这是您创建的唯一参考对象。通过此测试,您已经可以得出结论,由于参考对象的身份,已经收集了AAA
以前引用的对象,因此您已经阅读了“有意义的内容”。
如果要将更多信息与其关联,可行的方法是创建WeakReference
的子类,这正是WeakHashMap.Entry
的内容。在它的构造函数中,它调用super(key, queue);
,它与表达式new WeakReference(AAA, queue)
没有区别,第一个参数指定弱引用的对象。
因此,如果垃圾收集器的引用(WeakReference
)无法访问,则垃圾收集器会将专用的Entry
(即key
)对象排入队列。此时,无法再检索密钥,即其get()
方法将返回null
,但方法expungeStaleEntries()
无论如何都对密钥不感兴趣。它希望从表中删除Entry
实例,允许垃圾收集器回收Entry
实例本身以及可能引用的值,如果没有其它引用的话。这个子类记住以前计算的哈希码有帮助,因此地图不需要线性搜索。
答案 2 :(得分:0)
轮询ReferenceQueue
时,它会向对象返回Reference
个对象。入队操作由Reference#enqueue
完成,它将this
添加到队列中。因此,对于WeakReference
,由于它扩展Reference
,因此返回值可以转换为WeakReference
。
因此,在WeakHashMap
扩展Entry<K, V>
之后的WeakReference
实现中,poll的返回值可以转换为Entry<K, V>
,因为它是{{1}的子类这是WeakReference
的子类。换句话说,Reference
将添加到队列Reference#enqueue
,因此在this
实现中,它会将WeakHashMap
的实例排入队列。
请注意,类Entry<K, V>
没有引用键,因为它会导致强引用,因此GC不会最终确定它。它只保留散列,以便在正常情况下(即强烈引用密钥时)可以执行Entry<K, V>
的查询。