WeakHashMap如何在幕后工作

时间:2017-01-29 09:28:37

标签: java garbage-collection weak-references weakhashmap

我调查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。实际上我希望引用应该出现在队列中,但我无法读取有意义的内容,因为已经收集了对象(第二个问题)。

3 个答案:

答案 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>的查询。