美文网首页Java下午茶
WeakHashMap的Key-Value回收原理

WeakHashMap的Key-Value回收原理

作者: 徐志毅 | 来源:发表于2018-03-30 16:54 被阅读0次

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

    首先看WeakHashMap的Entry定义:

    //继承WeakReference
     private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
            V value;
            int hash;
            Entry<K,V> next;
    
            /**
             * Creates new entry.
             */
            Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> 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<Object> queue = new ReferenceQueue<>();
    

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

    
        /**
         * Returns the table after first expunging stale entries.
         */
        private Entry<K,V>[] 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<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;
                    }
                }
            }
        }
    

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

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

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

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

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

    相关文章

      网友评论

        本文标题:WeakHashMap的Key-Value回收原理

        本文链接:https://www.haomeiwen.com/subject/vjsicftx.html