关于tomcat源码包中 org.apache.el.util.ConcurrentCache 线程安全缓存,
是基于java.util.concurrent.ConcurrentHashMap 和 java.util.WeakHashMap 实现的,存放固定大小的数据 java.util.concurrent.ConcurrentHashMap ,当超过固定大小的数据存放在 java.util.WeakHashMap 中,当jvm进行垃圾回收时候,
java.util.WeakHashMap 的内容会被回收掉,以此保证多余的缓存不占用过多的内存。简单实用
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);
}
//非常住缓存如果不为空,那么尝试将内容存放在eden
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);
}
}
看代码的流程非常向浇灌田地的水车系统中的那个水桶
再看下 jdk 的 WeakHashMap 的实现
主要依赖ReferenceQueue<Object> queue 和 WeakReference ,这对组合是 jvm 发生垃圾回收时,将 WeakReference 的实现类存放到 ReferenceQueue 中,但是此时 WeakReference 的 T referent 已经jvm被回收。WeakHashMap 的get 和put 和 size 方法中会循环 ReferenceQueue 然后重新调整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;
}
}
}
}
网友评论