Java弱键集合WeakHashMap及ConcurrentCache原理详解
作者:刘Java
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:
- 经常使用的对象放入 eden 中,eden 使用 ConcurrentHashMap 实现,不用担心会被回收(伊甸园);
- 不常用的对象放入 longterm,longterm 使用 WeakHashMap 实现,这些不常使用的对象会被垃圾收集器回收。
- 当调用 get() 方法时,会先从 eden 区获取,如果没有找到的话再到 longterm 获取,当从 longterm 获取到就把对象放入 eden 中,从而保证经常被访问的节点不容易被回收。
- 当调用 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原理详解的文章就介绍到这了,更多相关WeakHashMap及ConcurrentCache原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!