简体   繁体   English

WeakHashMap的工作原理

[英]How WeakHashMap works under the hood

I invesigate WeakHashMap ource code to have more knowledge about WeakReference 我研究WeakHashMap我们的代码以了解有关WeakReference更多知识

I have found that entry looks like this: 我发现条目看起来像这样:

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;
        }
        ...

Thus when we create new entry we invoke super(key, queue); 因此,当我们创建新条目时,我们调用super(key, queue); . It is WeakReference constructor. 它是WeakReference构造函数。 As far I understand after object will be collected by GC the new reference(I believe it should be reference on key ) will be appeared in the queue. 据我了解,GC将收集对象之后 ,新引用(我相信应该是key上的引用)将出现在队列中。

Also I have noticed method which invokes on each operation: 我也注意到在每个操作上调用的方法:

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

Looks like we get (Entry<K,V>) from queue. 看起来我们从队列中得到了(Entry<K,V>) I don't know how to explain this( first question ). 我不知道该怎么解释( 第一个问题 )。 this code: 此代码:

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

always outputs null, because object already collected by GC 始终输出null,因为GC已经收集了对象

Also for me it was strange that we can have reference on Object which was already collected by GC. 同样让我感到奇怪的是,我们可以引用GC已经收集的Object。 Actually I expect that reference should appear in queue but I could not read something meaningful because object already collected( second question ). 实际上,我希望引用应该出现在队列中,但是由于对象已被收集( 第二个问题 ),我无法读取有意义的内容。

Looks like we get (Entry) from queue. 看起来我们从队列中获得了(条目)。 I don't know how to explain this 我不知道该怎么解释

queue.poll() gives you the reference instances you put into the queue through the reference constructor. queue.poll()为您提供了通过引用构造函数放入队列中的引用实例。 In this case it's Entry<K,V> extends WeakReference<Object> . 在这种情况下,它的Entry<K,V> extends WeakReference<Object>

Actually I expect that reference should appear in queue but I could not read something meaningful because object already collected 实际上,我希望引用应该出现在队列中,但是我无法读取有意义的内容,因为对象已被收集

You get the Reference object itself which you can use to do some cleanup, either through subclassing or by associating it with additional data eg through an auxiliary Map . 您可以获取Reference对象本身,可以通过子类化或通过将其与其他数据关联(例如通过辅助Map来进行一些清理。 The referee you can obtain through get while it is still alive is not relevant, the Reference object itself is. 您可以通过get仍然活着获取的裁判与此无关, Reference对象本身与此无关。

The queue returns the very reference object, you have created before. 队列返回您之前创建的参考对象。 So with your example code, after executing 因此,在执行之后,使用示例代码

Reference removedReference = queue.remove();

the expression removedReference == weakRef would evaluate to true , as that's the only reference object you've ever created. 表达式removedReference == weakRef计算结果为true ,因为这是您创建的唯一参考对象。 With this test, you can already conclude that the object formerly referenced by AAA has been collected, due to the identity of the reference object, so you already read “something meaningful”. 通过此测试,您可以得出结论,由于引用对象的身份,先前由AAA引用的对象已被收集,因此您已经阅读了“有意义的内容”。

If you want to associate more information with it, a viable way is to create a subclass of WeakReference , which is exactly what WeakHashMap.Entry is about. 如果您想与它关联更多信息,一种可行的方法是创建WeakReference的子类,而这正是WeakHashMap.Entry目的。 In its constructor, it calls super(key, queue); 在其构造函数中,它调用super(key, queue); , which is not different to your expression new WeakReference(AAA, queue) , the first argument specifies the weakly referenced object. ,与您的表达式new WeakReference(AAA, queue) ,第一个参数指定了弱引用的对象。

So the garbage collector will enqueue the specialized WeakReference , ie Entry , object if its referent (the key ) has become unreachable. 因此,如果垃圾回收的WeakReference对象( key )变得不可访问,则垃圾回收器会将其放入专用的WeakReference ,即Entry对象。 At this point, the key can't be retrieved anymore, ie its get() method will return null , but the method expungeStaleEntries() isn't interested in the key anyway. 此时,无法再检索到密钥,即其get()方法将返回null ,但方法expungeStaleEntries()始终对密钥不感兴趣。 It wants to remove the Entry instance from the table, allowing the garbage collector to reclaim the Entry instance itself and possibly the referenced value, if there are no other reference to it. 它希望从表中删除Entry实例,如果没有其他引用,则垃圾回收器可以回收Entry实例本身以及可能的引用值。 It helps that this subclass has remembered the previously calculated hash code, so the map doesn't need to search linearly. 这有助于该子类记住先前计算的哈希码,因此地图不需要线性搜索。

When a ReferenceQueue is polled it will return a Reference object to the referent. 轮询ReferenceQueue ,它将把Reference对象返回给被Reference对象。 The enqueue operation is done by Reference#enqueue which adds this to the queue. 入列操作是由做Reference#enqueue这增加了this队列。 Thus, for WeakReference , since it extends Reference the return value can be cast to WeakReference . 因此,对于WeakReference ,由于它扩展了Reference ,因此可以将返回值WeakReferenceWeakReference

So, in the WeakHashMap implementation since the Entry<K, V> extends WeakReference the return value for poll can be cast to Entry<K, V> as it is a subclass of WeakReference which is a subclass of Reference . 因此,在WeakHashMap实现中,由于Entry<K, V>扩展了WeakReference ,因此轮询的返回值可以转换为Entry<K, V>因为它是WeakReference的子类,而WeakReferenceReference的子类。 In other words, the Reference#enqueue will add to queue this thus in the WeakHashMap implementation it will enqueue the instance of Entry<K, V> . 换言之,该Reference#enqueue将添加到队列this因而在WeakHashMap执行将排队的实例Entry<K, V>

Do note that the class Entry<K, V> doesn't refer to key since it will lead to strong reference and thus GC will not finalize it. 请注意,类Entry<K, V>不会引用key,因为它将导致强引用,因此GC将不会最终确定它。 It only keeps the hash so that query for get can be executed under normal circumstances ie when the key is strongly referenced. 它仅保留哈希值,以便可以在正常情况下(即强烈引用键的情况下)执行对get查询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM