弱键集合WeakHashMap的原理以及ConcurrentCache的原理

Posted 刘Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了弱键集合WeakHashMap的原理以及ConcurrentCache的原理相关的知识,希望对你有一定的参考价值。

详细介绍了弱键集合WeakHashMap 的原理,以及tomcat中的ConcurrentCache的原理。

1 WeakHashMap 的原理

基于哈希表的Map接口实现,支持null键和值,但是WeakHashMap具有弱键,可用来实现缓存存储,在进行GC的时候会自动回收键值对。

WeakHashMap 的 Entry 节点继承自 WeakReference。put方法插入键值对时,创建Entry节点时,key被WeakReference引用,get方法获取key的时候,实际上是从WeakReference中获取的。Value正常引用存储,每次创建插入Entry 节点的时候,还会给WeakReference对象关联一个引用队列ReferenceQueue。

/**
 * 已清除的 WeakEntries 的引用队列
 */
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
/**
 * Entry继承了WeakReference,key被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;
    }

    @SuppressWarnings("unchecked")
    public K getKey() {
        return (K) WeakHashMap.unmaskNull(get());
    }

    public V getValue() {
        return value;
    }

    public V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

    //…………
}

key被WeakReference对象引用,它就是一个弱键。根据Java弱引用的特性,被WeakReference引用的对象在没有其他外部引用关联时,在下一次垃圾回收时将会回收该对象,并且其关联的WeakReference对象也会被加入到相关的引用队列中。

如果某个key因为没有其他外部引用被“回收”了getKey()方法就获取不到key了,就会返回null,其对应的Entry也会被加入到相关的引用队列中去了,此时这个Entry也就访问不到了,看起来整个Entry就像被回收了一样,但是此时这个Entry并没有被回收,因为它还被内部table数组引用了。

**无效的Entry怎么被清除呢? **实际上当我们每次需要操作WeakHashMap时,会先清除无效的Entry,位于expungeStaleEntries方法中。table中保存了全部的Entry键值对,而queue中保存被GC回收的Entry键值对,通过比对就能删除table中被GC回收的Entry键值对,这样就能清除无效的Entry了。

2 tomcat的ConcurrentCache

Tomcat的ConcurrentCache就使用了 WeakHashMap 来实现缓存功能。

ConcurrentCache 采取的是分代缓存,其内部保存了两个Map:

  1. 经常使用的对象放入 eden 中,eden 使用 ConcurrentHashMap 实现,不用担心会被回收(伊甸园);
  2. 不常用的对象放入 longterm,longterm 使用 WeakHashMap 实现,这些不常使用的对象会被垃圾收集器回收。
  3. 当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,从而保证经常被访问的节点不容易被回收。
  4. 当调用 put() 方法时,如果 eden 的大小超过了 size,那么就将 eden 中的所有对象都放入 longterm 中,利用虚拟机回收掉一部分不经常使用的对象。
public final class ConcurrentCache<K, V> {

    private final int size;

    private final Map<K, V> eden;

    private final Map<K, V> longterm;

    public ConcurrentCache(int size) {
        this.size = size;
        this.eden = new ConcurrentHashMap<>(size);
        this.longterm = new WeakHashMap<>(size);
    }

    public V get(K k) {
        V v = this.eden.get(k);
        if (v == null) {
            synchronized (longterm) {
                v = this.longterm.get(k);
            }
            if (v != null) {
                this.eden.put(k, v);
            }
        }
        return v;
    }

    public void put(K k, V v) {
        if (this.eden.size() >= size) {
            synchronized (longterm) {
                this.longterm.putAll(this.eden);
            }
            this.eden.clear();
        }
        this.eden.put(k, v);
    }
}

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

以上是关于弱键集合WeakHashMap的原理以及ConcurrentCache的原理的主要内容,如果未能解决你的问题,请参考以下文章

Java中的集合 实现Map接口的WeakHashMap

Java中的集合 实现Map接口的WeakHashMap

数据结构 - WeakHashMap

WeakHashMap和HashMap的区别

容器--WeakHashMap

#yyds干货盘点# Map - WeakHashMap源码解析