0

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

4

3 回答 3

0

当 aReferenceQueue被轮询时,它将返回一个Reference对象给所指对象。入队操作是通过Reference#enqueue添加this到队列中来完成的。因此,对于WeakReference, 因为它扩展Reference了返回值,所以可以强制转换为WeakReference

因此,在WeakHashMap实现中,由于Entry<K, V>扩展WeakReference了 poll 的返回值,因此可以强制转换Entry<K, V>为,因为它WeakReferenceReference. 换句话说,Reference#enqueue将添加到队列this中,因此在WeakHashMap实现中它会将Entry<K, V>.

请注意,该类Entry<K, V>不引用键,因为它会导致强引用,因此 GC 不会最终确定它。它只保留哈希,以便get在正常情况下可以执行查询,即当键被强引用时。

于 2018-06-11T20:41:09.693 回答
0

队列返回您之前创建的非常参考对象。因此,使用您的示例代码,在执行后

Reference removedReference = queue.remove();

该表达式的removedReference == weakRef计算结果为true,因为这是您创建的唯一引用对象。通过这个测试,您已经可以得出结论AAA,由于引用对象的身份,之前引用的对象已被收集,因此您已经阅读了“有意义的东西”。

如果您想将更多信息与它相关联,一个可行的方法是创建一个 的子类WeakReference,这正是它的意义WeakHashMap.Entry所在。在其构造函数中,它调用super(key, queue);与您的表达式没有什么不同new WeakReference(AAA, queue)的第一个参数指定弱引用对象。

因此,垃圾收集器将在其所指对象(the )变得无法访问时将专门的对象(WeakReference即对象)排入队列。此时,无法再检索密钥,即它的方法将返回,但该方法无论如何对密钥不感兴趣。它想从表中删除实例,允许垃圾收集器回收实例本身以及可能的引用值,如果没有其他引用。这有助于这个子类记住之前计算的哈希码,因此地图不需要线性搜索。Entrykeyget()nullexpungeStaleEntries()EntryEntry

于 2017-01-31T15:21:19.067 回答
0

看起来我们从队列中得到(条目)。我不知道如何解释

queue.poll()通过引用构造函数为您提供放入队列的引用实例。在这种情况下,它是Entry<K,V> extends WeakReference<Object>.

实际上我希望引用应该出现在队列中,但我无法读取有意义的内容,因为对象已经收集

Reference可以通过子类化或通过将其与附加数据(例如通过辅助)关联来获得可用于进行一些清理的对象本身Mapget你可以在它还活着的时候获得的裁判无关紧要,Reference对象本身才是。

于 2017-01-29T10:33:24.363 回答