WeakHashMap的Key-Value回收原理

WeakHashMap,它充分利用了WeakReference弱引用的特性,适合内存敏感的缓存实现场景。今天简单扒一扒它的实现原理。

首先看WeakHashMap的Entry定义:

//继承WeakReference
 private static class Entry extends WeakReference implements Map.Entry {
        V value;
        int hash;
        Entry next;

        /**
         * Creates new entry.
         */
        Entry(Object key, V value, ReferenceQueue queue, int hash, Entry next) {
            //这个super很关键,调用WeakReference的构造方法,使用key做referent
            super(key, queue);
            this.value = value;
            this.hash  = hash;
            this.next  = next;
        }

        @SuppressWarnings("unchecked")
        public K getKey() {
            return (K) WeakHashMap.unmaskNull(get());
        }

       //....其他省略
    }


Entry继承WeakReference,使用key作为WeakReference的弱引用,这意味着只要key没有其他地方持有(Map外不可达),map中的key就会被回收。目前来看,暂时只能回收key,那value是如何被回收的呢?在构造Entry的时候,还有一个特别的参数queue,这是WeakHashMap的一个成员变量:

    /**
     * Reference queue for cleared WeakEntries
     * 被清理的WeakEntries队列
     */
    private final ReferenceQueue queue = new ReferenceQueue<>();

有了这个队列,map就知道哪些key被GC回收掉了,这样就有办法控制value的回收了。


    /**
     * Returns the table after first expunging stale entries.
     */
    private Entry[] getTable() {
        expungeStaleEntries();
        return table;
    }

     /**
      * Expunges stale entries from the table.
      * 清理掉被GC的key对应Entries
      */
    private void expungeStaleEntries() {
        for (Object x; (x = queue.poll()) != null; ) {
            synchronized (queue) {
                @SuppressWarnings("unchecked")
                Entry e = (Entry) x;
                int i = indexFor(e.hash, table.length);

                Entry prev = table[i];
                Entry p = prev;
                while (p != null) {
                    Entry 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;
                }
            }
        }
    }

实际上value的回收是延迟与key的,仅仅是在key被GC后,把value置为null,并不是立即回收。

下面看一下expungeStaleEntries()的清理动作在哪些时候会被触发呢?

> expungeStaleEntries()
      > size()
      > getTable()
          > containsNullValue()
          > forEach(BiConsumer action)
          > get(Object key)
          > getEntry(Object key)
          > put(K key, V value)
          > remove(Object key)
          > removeMapping(Object o)
          > replaceAll(BiFunction function)
          > resize(int newCapacity)

从这些方法来看,基本上我们对WeakHashMap的操作都会触发清理。

WeakHashMap并不是我们想当然的使用了弱引用就可以在GC的时候被回收,里面的实现逻辑还是值得深入理解的。

你可能感兴趣的:(WeakHashMap的Key-Value回收原理)