jdk源码分析之WeakHashMap

基本原理

WeakHashMap特点是,当除了自身有对key的引用外,此key没有其他引用,那么WeakHashMap会在下次对WeakHashMap进行增删改查操作时及时丢弃该键值对,节约内存使用,此特性使得WeakHashMap非常适合构建缓存系统。
WeakHashMap是主要通过expungeStaleEntries函数的来实现移除其内部不用的entry从而达到的自动释放内存的目的。基本上只要对WeakHashMap的内容进行访问就会调用expungeStaleEntries函数,从而达到清除不再被外部引用的key对应的entry键值对。如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那么因为没有机会调用expungeStaleEntries函数,因此并不会回收不再被外部引用的key对应的entry。

Entry键值对

WeakHashMap的键值对Entry继承自WeakReference,并实现了Map.Entry

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>

当弱引用指向的对象只能通过弱引用(没有强引用或弱引用)访问时,GC会清理掉该对象,之后,引用对象会被放到ReferenceQueue中。在Entry的构造函数中可以得知,通过super(key, queue)将key保存为弱引用,通过this.value = value将value保存为强引用。当key中的引用被gc掉之后,在下次访问WeakHashMap(调用expungeStaleEntries函数)时相应的entry也会自动被移除。

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

expungeStaleEntries():清除过期的条目

从ReferenceQueue中取出过期的entry,从WeakHashMap找到对应的entry,逐一删除

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

首先通过循环一直从queue中取过期entry直到取完为止

    for (Object x; (x = queue.poll()) != null; )

然后通过加锁queue进行删除过期entry的操作

    synchronized (queue) {
    ...
    }

在同步代码块中先把从queue中取出的Object类型的数据强制转化为Entry对象e,然后计算此entry在桶的位置(table数组的下标i),然后开始遍历entry链表,如果此entry是链表头,设置此entry的后继为新的链表头

              if (prev == e)
                  table[i] = next;

否则将此entry的前序节点的后继指针指向此entry的后继节点

               else
                   prev.next = next;

最后设置被删除的entry的value为null,加速垃圾回收,接着修改size

               e.value = null; // Help GC
               size--;

访问WeakHashMap时,对过期条目进行清除

这里写图片描述
这里写图片描述
可以看到对WeakHashMap的增删改查操作都会直接或者间接的调用expungeStaleEntries()方法,达到及时清除过期entry的目的。
此特性会到导致两次调用size、get等方法可能会返回不一致的数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值