WeakHashMap原理与应用场景详解

WeakHashMap原理与应用场景详解-mikechen

WeakHashMap直译就是虚弱的 HashMap,所以核心点是弱引用WeakReference,这是重点@mikechen

什么是WeakHashMap

Map 的子类常见的有 HashMap、Hashtable、ConcurrentHashMap等。

而WeakHashMap 继承于AbstractMap,实现了Map接口,和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。

不过WeakHashMap的键是“弱键”,在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。

跟HashMap一样,WeakHashMap也是一个哈希表,它比HashMap多出来的功能就是,存储在WeakHashMap中且没有被其他地方引用的key可以被GC回收,而如果把key保存在HashMap中,则无论这个key在其他地方有没有被引用,这个key一直驻留在JVM内存。

 

WeakHashMap与HashMap的区别

从类定义上来看,它和普通的HashMap一样,继承了AbstractMap类和实现了Map接口,也就是说它有着与HashMap差不多的功能。

那么既然jdk已经提供了HashMap,为什么还要再提供一个WeakHashMap呢?

看一个例子,如果需要一个Cache,你肯定会面临一个问题,就是所有数据不可能都放到Cache里,或者放到Cache里性价比太低了。

这个时候你可能很快就想到了各种Cache数据过期策略,目前也有一些优秀的包提供了功能丰富的Cache,它支持数据定期过期、LRU、LFU等策略,但它任然有可能会导致有用的数据被淘汰,没用的数据迟迟不淘汰(如果策略使用得当的情况下这都是小概率事件)。

如果我现在说有种机制,可以让你Cache里不用的key数据自动清理掉,用的还留着,没有误杀也没有漏杀你信不信!没错WeakHashMap就是能实现这种功能的东西,这也是它和普通的HashMap不同的地方——它有自清理的机制。

Jvm提供了一种机制能让我们感知到一个对象是否已经变成了垃圾对象,这就是WeakReference,不了解WeakReference的可以看下Java四大引用详解:强引用、软引用、弱引用、虚引用这篇文章。

某个WeakReference对象所指向的对象如果被判定为垃圾对象,Jvm会将该WeakReference对象放到一个ReferenceQueue里,接着遍历queue进行删除。

 

WeakHashMap实现原理

谈WeakHashMap原理得从WeakReference弱引用说起。

WeakHashMap具有弱引用的特点,随时被回收对象,发生GC时 WeakHashMap是如何将Entry移除的呢?

WeakHashMap内部的Entry继承了WeakReference,即弱引用,所以就具有了弱引用的特点,随时可能被回收,源码如下:

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

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

大家都知道HashMap实现里面有个Entry数组,WeakHashMap也一样也有一个Entry数组,但是此Entry与彼Entry有些不一样。

WeakHashMap的Entry是继承WeakReference,这样一来,整个Entry就是一个WeakReference。

这个“弱键”的原理呢?

大致上就是通过WeakReference和ReferenceQueue实现的。

// 用于存储需要清理的引用对象
    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();

WeakHashMap的key是“弱键”,即是WeakReference类型的,而ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。

实现步骤是:

01:新建WeakHashMap,将“键值对”添加到WeakHashMap中。

02:当某“弱键”不再被其它对象引用,并被GC回收时,在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。

03: 当下一次我们需要操作WeakHashMap时,会先同步table和queue,table中保存了全部的键值对,而queue中保存被GC回收的键值对,同步它们,就是删除table中被GC回收的键值对。

这就是“弱键”如何被自动从WeakHashMap中删除的步骤。

 

WeakHashMap的应用场景

1.缓存使用

由于 WeakHashMap 是弱引用,因此适合在缓存中使用,当内存不足GC的时候,会清理不用的引用达到释放内存的目的。

 

2.诊断工具

在阿里开源的Java诊断工具Arthas中使用了WeakHashMap做类-字节码的缓存。

// 类-字节码缓存
   private final static Map<Class<?>/*Class*/, byte[]/*bytes of Class*/> classBytesCache
           = new WeakHashMap<Class<?>, byte[]>();

 

 

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法