【问题标题】:WeakHashMap how is the entry _actually_ found after the reference is put on the ReferenceQueueWeakHashMap 在将引用放在ReferenceQueue 上后_actually_ 是如何找到条目的
【发布时间】:2020-06-22 14:07:54
【问题描述】:

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 现在消失了,它如何“对他们采取行动”?毕竟这是...HASHMap,所以要移除元素,它必须知道它是hashCode。你怎么知道hashCode 的东西现在已经消失了?

【问题讨论】:

    标签: java garbage-collection weak-references weakhashmap


    【解决方案1】:

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

    由于 referent 确实消失了,并且您无法计算其上的 hashCode,因此您可以使用 == 搜索 Reference,跨越 所有 Map 中的条目。在发布的示例中,您可以添加如下几行:

        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 是不可能的,但这没关系。

    【讨论】:

      猜你喜欢
      • 2021-04-25
      • 2020-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-28
      相关资源
      最近更新 更多