欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识
原文出处: SylvanasSun's Blog

WeakHashMap是一个基于Map接话柄现的散列表,实现细节与HashMap雷同(都有负载因子、散列函数等等,但没有HashMap那么多优化手段),它的非凡之处在于每个key都是一个弱引用。

首先我们要大白什么是弱引用,Java将引用分为四类(从JDK1.2开始),强度依次逐渐削弱:

  • 强引用: 就是泛泛利用的普通引用工具,譬喻Object obj = new Object(),这就是一个强引用,强引用只要还存在,就不会被垃圾收集器接纳。
  • 软引用: 软引用暗示一个尚有用但并非必须的工具,不像强引用,它还需要通过SoftReference类来间接引用方针工具(除了强引用都是如此)。被软引用关联的工具,昆山软件开发,在将要产生内存溢出异常之前,会被放入接纳范畴之中以举办第二次接纳(假如第二次接纳之后依旧没有足够的内存,那么就会抛出OOM异常)。
  • 弱引用: 同样是暗示一个非必须的工具,但要比软引用的强度还要弱,需要通过WeakReference类来间接引用方针工具。被弱引用关联的工具只能存活到下一次垃圾接纳产生之前,当触发垃圾接纳时,无论当前内存是否足够,城市接纳掉只被弱引用关联的工具(假如这个工具还被强引用所引用,那么就不会被接纳)。
  • 虚引用: 这是一种最弱的引用干系,需要通过PhantomReference类来间接引用方针工具。一个工具是否有虚引用的存在,昆山软件开发,完全不会对其保留时间组成影响,也无法通过虚引用来得到工具实例。虚引用的独一浸染就是能在这个工具被接纳时收到一个系统通知(团结ReferenceQueue利用)。基于这点可以通过虚引用来实现工具的析构函数,这比利用finalize()函数是要靠谱多了。
  • WeakHashMap适适用来当做一个缓存来利用。假设你的缓存系统是基于强引用实现的,那么你就必需以手动(可能用一条线程来不绝轮询)的方法来删除一个无效的缓存项,而基于弱引用实现的缓存项只要没被其他强引用工具关联,就会被直接放入接纳行列。

    需要留意的是,只有key是被弱引用关联的,而value一般都是一个强引用工具。因此,需要确保value没有关联到它的key,不然会对key的接纳发生阻碍。在极度的环境下,一个value工具A引用了另一个key工具D,劳务派遣管理系统,而与D相对应的value工具C又反过来引用了与A相对应的key工具B,这就会发生一个引用轮回,导致D与B都无法被正常接纳。想要办理这个问题,就只能把value也酿成一个弱引用,譬喻m.put(key, new WeakReference(value)),弱引用之间的相互引用不会发生影响。

    查找操纵的实现跟HashMap对比简朴了很多,只要读懂了HashMap,根基都能看懂,源码如下:

    /**
     * Value representing null keys inside tables.
     */
    private static final Object NULL_KEY = new Object();
    /**
     * Use NULL_KEY for key if it is null.
     */
    private static Object maskNull(Object key) {
        return (key == null) ? NULL_KEY : key;
    }
    /**
     * Returns index for hash code h.
     */
    private static int indexFor(int h, int length) {
        return h & (length-1);
    }
    public V get(Object key) {
        // WeakHashMap答允null key与null value
        // null key会被替换为一个虚拟值
        Object k = maskNull(key); 
        int h = hash(k);
        Entry<K,V>[] tab = getTable();
        int index = indexFor(h, tab.length);
        Entry<K,V> e = tab[index];
        // 遍历链表
        while (e != null) {
            if (e.hash == h && eq(k, e.get()))
                return e.value;
            e = e.next;
        }
        return null;
    }

    尽量key是一个弱引用,但仍需手动地接纳那些已经无效的Entry。这个操纵会在getTable()函数中执行,不管是查找、添加照旧删除,都需要挪用getTable()来得到buckets数组,所以这是种防备内存泄漏的被动掩护法子。

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    Entry<K,V>[] table;
    /**
     * Reference queue for cleared WeakEntries
     */
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    /**
     * Expunges stale entries from the table.
     */
    private void expungeStaleEntries() {
        // 遍历ReferenceQueue,然后清理table中无效的Entry
        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;
                }
            }
        }
    }
    /**
     * Returns the table after first expunging stale entries.
     */
    private Entry<K,V>[] getTable() {
        expungeStaleEntries();
        return table;
    }