WeakHashMap在引用被放入ReferenceQueue之后,条目_实际_是如何找到的



WeakHashMap的工作原理与WeakReferenceReferenceQueue的耦合非常相似——对此没有任何消息。下面是一个简单的例子,说明它应该如何工作:

public class ReferenceQueuePlayground {
public static void main(String[] args) {
ReferenceQueue<Referent> q = new ReferenceQueue<>();
Referent ref = new Referent();
WeakReference<Referent> weak = new WeakReference<>(ref, q);
ref = null;
// wait for GC to reclaim Referent
while (weak.get() != null) {
System.gc();
}
// this might return null, because ReferenceQueue is notified asynchronously
// but I am assuming the happy path here 
Reference<? extends Referent> reference = q.poll();
// this will be false
System.out.println(reference == null);
// this will be true
System.out.println(reference.get() == null);
}
@RequiredArgsConstructor
@Getter
static class Referent {
}
}

这正是WeakHashMap的工作方式——当referent被回收,reference被放在ReferenceQueue上时,它会得到通知。在随后的某个操作中,调用expungeStaleEntries,它基本上会从该ReferenceQueue中逐个获取元素并对其进行操作。

我的问题是:如果referent现在不在了,它怎么能"对他们采取行动"?这毕竟是一个...HASHMap,所以为了删除元素,它必须知道它是hashCode。你怎么能知道现在已经不存在的东西的hashCode

有两种方法。第一个是线性搜索

由于引用确实不存在,并且您无法在其上计算hashCode,因此您可以在Map中的所有条目中搜索具有==Reference。在发布的示例中,您可以添加几行,如:

WeakReference<Referent> weak = new WeakReference<>(ref, q);
// <--- this
System.out.println(weak);
ref = null;
while (weak.get() != null) {
System.out.println("not yet");
System.gc();
}
Reference<? extends Referent> reference = q.poll();
// <---- and this
System.out.println(reference);

这两者将打印相同的东西,这是完全有意义的。因此,理论上,WeakHashMap可以取其得到的reference(实际上是Entry(并遍历其内部阵列,直到找到匹配。

显然,这将是缓慢的。

第二种方法是WeakHashMap实际采用的方法。当第一次创建Entry时,它计算hashCode并将其放入本地字段:

/**
* 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;
}

在这一点上,它知道Key,因此它可以计算hashCode。稍后调用expungeStaleEntries时:

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);

它已经知道hashCode,因为它是在此之前计算的。它不知道Key,但也不需要它。

这将有助于找到这个条目所在的bucket,但要真正找到特定的条目,它只能在条目本身上使用==。由于Key已经消失,equals是不可能的,但这并不重要。

相关内容

  • 没有找到相关文章

最新更新