How WeakHashMap works under the hood

眉间皱痕 提交于 2019-12-02 19:32:40

问题


I invesigate WeakHashMap ource code to have more knowledge about 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);. It is WeakReference constructor. 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.

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

Also for me it was strange that we can have reference on Object which was already collected by GC. Actually I expect that reference should appear in queue but I could not read something meaningful because object already collected(second question).


回答1:


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. In this case it's 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 e.g. through an auxiliary Map. The referee you can obtain through get while it is still alive is not relevant, the Reference object itself is.




回答2:


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. 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”.

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. In its constructor, it calls super(key, queue);, which is not different to your expression new WeakReference(AAA, queue), the first argument specifies the weakly referenced object.

So the garbage collector will enqueue the specialized WeakReference, i.e. Entry, object if its referent (the key) has become unreachable. At this point, the key can’t be retrieved anymore, i.e. its get() method will return null, but the method expungeStaleEntries() isn’t interested in the key anyway. 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. It helps that this subclass has remembered the previously calculated hash code, so the map doesn’t need to search linearly.




回答3:


When a ReferenceQueue is polled it will return a Reference object to the referent. The enqueue operation is done by Reference#enqueue which adds this to the queue. Thus, for WeakReference, since it extends Reference the return value can be cast to WeakReference.

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

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. It only keeps the hash so that query for get can be executed under normal circumstances i.e. when the key is strongly referenced.



来源:https://stackoverflow.com/questions/41919394/how-weakhashmap-works-under-the-hood

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!