美文网首页Java
WeakReference 在 WeakHashMap 和 T

WeakReference 在 WeakHashMap 和 T

作者: Zentopia | 来源:发表于2020-04-02 16:38 被阅读0次

    WeakHashMap

    static class Entry extends WeakReference<ThreadLocal<?>> {
               /** The value associated with this ThreadLocal. */
               Object value;
    
               Entry(ThreadLocal<?> k, Object v) {
                   super(k);
                   value = v;
               }
           }
    

    TheadLocalMap

     private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
            V value;
            final int hash;
            Entry<K,V> next;
    ...
    }
    

    WeakHashMapThreadLocalMap 中 Entry 的 key 使用的都是弱引用,如果 key 没有被强引用,那么在发生 GC 的情况下,key 对象会被回收。所以弱引用通常用来指向使用周期短暂的数据,以提高内存的使用效率。

    key 被回收后,WeakHashMapThreadLocalMap 中就存在 key 为 null 的 entry,通常称之为 stale entry。Entry 中的 value 也无法被使用,如果被不能清理就会发生内存泄漏。

    WeakHashMapThreadLocalMap 中,对 stale entry 的清理方式是不同的。

    WeakHashMap 使用 expungeStaleEntries() 方法来清理:

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

    其中的 queue 为 reference queue,用来存放需要被清理的 WeakEntries,即那些 stale entries。通过遍历来移除 table 中的 stale entries。

    expungeStaleEntries() 方法会在 table 获取、扩容等场景下调用。

    在 LocalThreadMap 中也有相应的 expungeStaleEntries() 方法:

    /**
             * Expunge a stale entry by rehashing any possibly colliding entries
             * lying between staleSlot and the next null slot.  This also expunges
             * any other stale entries encountered before the trailing null.  See
             * Knuth, Section 6.4
             *
             * @param staleSlot index of slot known to have null key
             * @return the index of the next null slot after staleSlot
             * (all between staleSlot and this slot will have been checked
             * for expunging).
             */
            private int expungeStaleEntry(int staleSlot) {
                Entry[] tab = table;
                int len = tab.length;
    
                // expunge entry at staleSlot
                tab[staleSlot].value = null;
                tab[staleSlot] = null;
                size--;
    
                // Rehash until we encounter null
                Entry e;
                int i;
                for (i = nextIndex(staleSlot, len);
                     (e = tab[i]) != null;
                     i = nextIndex(i, len)) {
                    ThreadLocal<?> k = e.get();
                    if (k == null) {
                        e.value = null;
                        tab[i] = null;
                        size--;
                    } else {
                        int h = k.threadLocalHashCode & (len - 1);
                        if (h != i) {
                            tab[i] = null;
    
                            // Unlike Knuth 6.4 Algorithm R, we must scan until
                            // null because multiple entries could have been stale.
                            while (tab[h] != null)
                                h = nextIndex(h, len);
                            tab[h] = e;
                        }
                    }
                }
                return i;
            }
    

    expungeStaleEntries() 在扩容rehash、set 等场景下会被调用,但是从源码中可以发现,expungeStaleEntries() 方法被调用的条件比较苛刻。因此,在使用 ThreadLocal 时,通常需要我们显示调用 ThreadLocal 的 remove() 方法来触发 expungeStaleEntries()。

    当使用线程池时,因为存在线程重用机制,所以在使用完ThreadLocal 后,需要调用其 remove() 方法来避免内存泄漏。

    相关文章

      网友评论

        本文标题:WeakReference 在 WeakHashMap 和 T

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