WeakHashMap 将引用放入 ReferenceQueue 后如何实际找到条目
WeakHashMap how is the entry _actually_ found after the reference is put on the ReferenceQueue
A WeakHashMap
的工作原理与 WeakReference
与 ReferenceQueue
的结合非常相似 - 关于此的新闻为零。这是它应该如何工作的精简示例:
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
现在消失了,怎么会 "act on them" 呢?毕竟这是一个 ...HASHMap
,所以为了删除该元素,它必须知道它是 hashCode
。你怎么知道现在已经消失的东西的 hashCode
?
有两种方法。第一个是 线性搜索 。
由于 referent 确实没有了,你无法计算它上面的 hashCode
,你可以用 [=15] 搜索 Reference
=],跨越 Map
中的 all 个条目。在发布的示例中,您可以添加几行:
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
,但也不需要它。
这将有助于找到这个条目所在的桶,但要真正找到特定的条目,它只能在条目本身上使用 ==
。由于 Key
消失了,equals
是不可能的,但这并不重要。
A WeakHashMap
的工作原理与 WeakReference
与 ReferenceQueue
的结合非常相似 - 关于此的新闻为零。这是它应该如何工作的精简示例:
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
现在消失了,怎么会 "act on them" 呢?毕竟这是一个 ...HASHMap
,所以为了删除该元素,它必须知道它是 hashCode
。你怎么知道现在已经消失的东西的 hashCode
?
有两种方法。第一个是 线性搜索 。
由于 referent 确实没有了,你无法计算它上面的 hashCode
,你可以用 [=15] 搜索 Reference
=],跨越 Map
中的 all 个条目。在发布的示例中,您可以添加几行:
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
,但也不需要它。
这将有助于找到这个条目所在的桶,但要真正找到特定的条目,它只能在条目本身上使用 ==
。由于 Key
消失了,equals
是不可能的,但这并不重要。