Java的WeakHashMap和caching:为什么它引用的是键,而不是值?

Java的WeakHashMap经常被认为对于caching很有用。 看起来很奇怪,它的弱引用是用地图的键来定义的,而不是它的值。 我的意思是,这是我想要caching的值,我想要垃圾收集一次没有人除了caching强烈引用它们,不是吗?

在哪个方面有助于保持对键的弱引用? 如果你做一个ExpensiveObject o = weakHashMap.get("some_key") ,那么我希望caching保持到'o',直到调用者不再拥有强引用,我不在乎string对象“some_key”。

我错过了什么吗?

WeakHashMap作为一个caching是没有用的,至less大多数人是这样想的。 正如你所说,它使用的是弱 ,而不是弱 ,所以它不是为大多数人想要使用它而devise的(事实上,我看到有人用它来错误地)。

WeakHashMap主要用于保存关于不受其控制的生命周期的对象的元数据。 例如,如果你有一大堆对象通过你的类,并且你想跟踪额外的关于他们的数据,而不需要在他们超出范围时得到通知,并且没有你的引用让他们活着。

一个简单的例子(和我以前用过的)可能是这样的:

 WeakHashMap<Thread, SomeMetaData> 

在那里你可以跟踪系统中的各种线程在做什么; 当线程死亡时,条目将被静默地从你的地图中删除,并且如果你是最后一个引用,你将不会阻止垃圾收集。 然后,您可以遍历该映射中的条目,以查找有关系统中活动线程的元数据。

看不到caching中的WeakHashMap! 了解更多信息。

对于您所使用的cachingtypes,请使用专用的caching系统(例如EHCache )或查看Googlecollections 夹的MapMaker类 ; 就像是

 new MapMaker().weakValues().makeMap(); 

会做你以后的事情,或者如果你想变得有趣,你可以添加超时到期:

 new MapMaker().weakValues().expiration(5, TimeUnit.MINUTES).makeMap(); 

WeakHashMap的主要用途是当你的键映射消失时你想要消失的映射。 caching是相反的 – 你有映射,当它们的值消失时,你想消失。

对于一个caching,你想要的是一个Map<K,SoftReference<V>> 。 内存SoftReference将被垃圾收集。 (将其与WeakReference对比,只要不存在对被引用对象的硬引用,就可以将其清除)。您希望引用在caching中是软的(至less在键值映射不去的地方)陈旧的),因为如果你稍后再查找它们,你的值将有可能仍然在caching中。 如果引用很弱,那么你的价值就会马上被刷新,从而破坏了caching的目的。

为了方便起见,您可能希望隐藏Map实现中的SoftReference值,以便caching看起来types为<K,V>而不是<K,SoftReference<V>> 。 如果你想这样做, 这个问题有networking上可用的实现build议。

还要注意的是,当你在一个Map使用SoftReference值时,你必须做一些操作去除已经清除了SoftReferences的键值对,否则你的Map将会泄漏内存。

另一件要考虑的事情是,如果采用Map<K, WeakReference<V>>方法,值可能会消失,但映射不会。 根据使用情况,最终可能会得到一个包含许多弱引用已被GC化的条目的Map。

您需要两个映射:一个映射caching键和弱引用值,另一个映射弱引用值和键之间的相反方向。 而且你需要一个引用队列和一个清理线程。

当引用的对象不能再被访问时,弱引用有能力将引用移动到队列中。 这个队列必须被清理线程清除。 而清理是必要的,以获得参考的关键。 这就是为什么第二张地图是必需的。

以下示例显示如何使用弱引用的哈希映射创buildcaching。 当你运行程序时,你会得到如下输出:

 $ javac -Xlint:unchecked Cache.java && java Cache
 {even:[2,4,6],odd:[1,3,5]}
 {even:[2,4,6]}

第一行显示了对奇数列表的引用已被删除之前caching的内容以及赔率被删除后的第二行。

这是代码:

 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(); } }