Java的WeakHashMap中经常被引用作为缓存有用。 虽然它的弱引用在地图上的钥匙,而不是它的值来定义这似乎很奇怪。 我的意思是,这是我想要缓存的值,这是我想要得到的垃圾收集一次没有其他人,除了缓存强烈引用它们,不是吗?
以何种方式它有助于保持到钥匙弱引用? 如果你做一个ExpensiveObject o = weakHashMap.get("some_key")
那么我希望缓存保存到“O”,直到调用者没有持有强引用了,我不在乎在所有关于字符串对象“some_key”。
我缺少的东西吗?
WeakHashMap中不作为高速缓存,至少像大多数人认为它有用。 正如你所说的,它使用的弱密钥 ,不弱的值 ,所以它不适合大多数人想用它(和,其实,我见过的人使用它的,不正确的)。
WeakHashMap中是最有用保持的元数据对象的生命周期,你不用管。 举例来说,如果你有一堆穿过你的类的对象,你想跟踪关于他们的额外数据,而无需外出时的范围进行通报,并没有你提到他们让他们活着。
一个简单的例子(和一个我以前用过的)可能是这样的:
WeakHashMap<Thread, SomeMetaData>
在那里你可以跟踪哪些不同的线程在您的系统正在做的; 当线程死亡,条目将被悄悄从地图上删除,你就不会被垃圾收集,如果你是最后一个引用保持线程。 然后,您可以遍历项目在地图上找出您对您的系统活动线程哪些元数据。
见WeakHashMap中在不缓存! 欲获得更多信息。
对于缓存的类型你之后,无论是使用专用的高速缓冲存储器系统(例如的EHCache )或者看看谷歌的集合 地图制作工具类 ; 就像是
new MapMaker().weakValues().makeMap();
会做你以后,或者如果你想获得看上你可以添加定时到期:
new MapMaker().weakValues().expiration(5, TimeUnit.MINUTES).makeMap();
对于主要使用WeakHashMap
是,当你拥有了你想要的时候他们的钥匙消失消失映射。 缓存是相反的---你要消失的时候他们的价值消失的映射。
对于缓存,你想要的是一个Map<K,SoftReference<V>>
。 一个SoftReference
当内存变得紧将是垃圾收集。 (与此对比WeakReference
,可只要不再有硬参考其所指清除。)您希望您的引用是在一个高速缓存(软在一个至少在键-值映射不走陈旧的),从那以后有机会的话,你的价值仍然会在缓存中,如果你找他们以后。 如果引用是微弱的,而不是,你的价值观会马上gc'd,击败缓存的目的。
为方便起见,您可能希望隐藏SoftReference
你的内部价值观Map
的实现,让你的缓存似乎型<K,V>
代替<K,SoftReference<V>>
。 如果你想这样做, 这个问题对在网络上可用的实现建议。
还要注意的是,当你使用SoftReference
一个数值Map
,你必须做一些事情来手动删除它有自己的键值对SoftReferences
清除---否则你的Map
只会越来越庞大,直到永远,泄漏内存。
另一个要考虑的是,如果你把Map<K, WeakReference<V>>
办法,值可能会消失,但映射不会。 根据使用情况,您可能因此结了包含许多条目,其弱引用已经GC'd地图。
您需要两个地图:一个它的缓存键之间映射弱引用在弱引用值与键之间的相反方向映射值和一个。 你需要一个引用队列和清除线程。
弱引用有参考移动到队列时被引用的对象不能访问任何更长的能力。 这个队列必须由清理线程排出。 而对于清理,有必要得到一个参考的关键。 这就是为什么需要第二个地图的原因。
下面的示例演示如何创建具有弱引用的哈希映射中的缓存。 当你运行该程序将得到以下的输出:
$ javac -Xlint:unchecked Cache.java && java Cache
{even: [2, 4, 6], odd: [1, 3, 5]}
{even: [2, 4, 6]}
第一行显示的缓存中的内容之前,参考奇数名单已经被删除其胜算之后的第二行已被删除。
这是代码:
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class Cache<K,V>
{
ReferenceQueue<V> queue = null;
Map<K,WeakReference<V>> values = null;
Map<WeakReference<V>,K> keys = null;
Thread cleanup = null;
Cache ()
{
queue = new ReferenceQueue<V>();
keys = Collections.synchronizedMap (new HashMap<WeakReference<V>,K>());
values = Collections.synchronizedMap (new HashMap<K,WeakReference<V>>());
cleanup = new Thread() {
public void run() {
try {
for (;;) {
@SuppressWarnings("unchecked")
WeakReference<V> ref = (WeakReference<V>)queue.remove();
K key = keys.get(ref);
keys.remove(ref);
values.remove(key);
}
}
catch (InterruptedException e) {}
}
};
cleanup.setDaemon (true);
cleanup.start();
}
void stop () {
cleanup.interrupt();
}
V get (K key) {
return values.get(key).get();
}
void put (K key, V value) {
WeakReference<V> ref = new WeakReference<V>(value, queue);
keys.put (ref, key);
values.put (key, ref);
}
public String toString() {
StringBuilder str = new StringBuilder();
str.append ("{");
boolean first = true;
for (Map.Entry<K,WeakReference<V>> entry : values.entrySet()) {
if (first)
first = false;
else
str.append (", ");
str.append (entry.getKey());
str.append (": ");
str.append (entry.getValue().get());
}
str.append ("}");
return str.toString();
}
static void gc (int loop, int delay) throws Exception
{
for (int n = loop; n > 0; n--) {
Thread.sleep(delay);
System.gc(); // <- obstinate donkey
}
}
public static void main (String[] args) throws Exception
{
// Create the cache
Cache<String,List> c = new Cache<String,List>();
// Create some values
List odd = Arrays.asList(new Object[]{1,3,5});
List even = Arrays.asList(new Object[]{2,4,6});
// Save them in the cache
c.put ("odd", odd);
c.put ("even", even);
// Display the cache contents
System.out.println (c);
// Erase one value;
odd = null;
// Force garbage collection
gc (10, 10);
// Display the cache again
System.out.println (c);
// Stop cleanup thread
c.stop();
}
}
文章来源: Java's WeakHashMap and caching: Why is it referencing the keys, not the values?