你什么时候使用WeakHashMap或者WeakReference?
弱引用的使用是我从来没有见过的实现,所以我想弄清楚它们的用例是什么,以及如何实现工作。 何时需要使用WeakHashMap
或WeakReference
,它是如何使用的?
强引用的一个问题是caching,特别是像图像这样的非常大的结构。 假设你有一个应用程序必须使用用户提供的图像,就像我工作的网站devise工具一样。 当然,你想caching这些图像,因为从磁盘加载它们是非常昂贵的,你想避免在内存中同时存储两个(可能是巨大的)图像的可能性。
由于图像caching会阻止我们在不需要的时候重新加载图像,因此您将很快意识到caching应该始终包含对已经在内存中的任何图像的引用。 但是,对于普通的强引用,引用本身会强制映像保留在内存中,这就要求您以某种方式确定内存中不再需要映像的时间,并将其从caching中删除,以便它有资格进行垃圾回收。 你不得不复制垃圾收集器的行为,并手动确定一个对象是否应该在内存中。
理解弱的参考 ,Ethan Nicholas
WeakReference
和SoftReference
之间的区别是需要明确的区别。
基本上, WeakReference
会被JVM热切的引用GC-d,一旦被引用的对象没有硬引用。 另一方面, SoftReference
d对象往往会被垃圾收集器遗留下来,直到真正需要回收内存为止。
WeakReference
中保存值的caching是无用的(在WeakHashMap
,它是被弱引用的键)。 当你想要实现一个可以随着可用内存增长和缩小的caching时, SoftReferences
是很有用的
WeakReference
和WeakHashMap
的一个常见用途是为对象添加属性。 偶尔你想添加一些function或数据到一个对象,但是子类化和/或组合不是一种select,在这种情况下,显而易见的事情就是创build一个hashmap,将你想要扩展的对象链接到你想要添加的属性。 那么只要你需要财产,你可以在地图上查找它。 但是,如果要添加属性的对象倾向于被破坏和创build,那么最终可能会导致地图中的很多旧对象占用大量内存。
如果你使用了WeakHashMap
那么当你的地图不再被你的程序的其余部分使用时,这些对象就会离开你的地图,这就是所期望的行为。
我不得不这样做来添加一些数据到java.awt.Component
来解决1.4.2到1.5之间的JRE的变化,我可以通过inheritance我感兴趣的每个组件来修复它int( JButton
, JFrame
, JPanel
。 …),但是这个代码less得多。
WeakHashMap
和WeakReference
另一个有用的例子是监听器registry实现 。
当你创build一些想听某些事件的东西的时候,通常你会注册一个监听器,比如
manager.registerListener(myListenerImpl);
如果manager
用WeakReference
存储你的监听manager
,这意味着你不需要例如用一个manager.removeListener(myListenerImpl)
来移除该注册器,因为一旦你的监听器或者拥有监听器的组件不可用,它将被自动移除。
当然,你仍然可以手动删除你的监听器, 但是如果你不这样做,或者你忘了它,它不会导致内存泄漏,也不会阻止你的监听器被垃圾收集。
WeakHashMap
在哪里出现?
希望将注册监听器存储为WeakReference
的监听器registry需要一个集合来存储这些引用。 标准Java库中没有WeakHashSet
实现,只有WeakHashMap
但我们可以很容易地使用后者来实现第一个function:
Set<ListenerType> listenerSet = Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());
使用这个listenerSet
来注册一个新的监听器,你只需要将它添加到这个集合中,即使它没有被明确的移除,如果这个监听器不再被引用,它将被JVM自动移除。
例如,如果您想跟踪某个类创build的所有对象。 为了仍然允许这些对象被垃圾收集,你保留一个弱对象的引用而不是对象本身的列表/映射。
现在,如果有人能解释我的幻影参考,我会很高兴…
我对WeakReferences的一个真实世界的使用是,如果你有一个很less使用的单个非常大的对象。 当不需要的时候,你不想把它保存在内存中; 但是,如果另一个线程需要相同的对象,你不需要在内存中的两个。 你可以在某个地方保留一个对象的弱引用,并且在使用它的方法中有很多引用。 当方法都完成时,对象将被收集。
这篇博文演示了两个类的使用: Java:在一个ID上进行同步 。 用法如下所示:
private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider(); public void performTask(String resourceId) { IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId); synchronized (mutext) { // look up the resource and do something with it } }
IdMutextProvider提供基于ID的对象进行同步。 要求是:
- 必须返回对同一对象的引用,以便同时使用等效ID
- 必须为不同的ID返回不同的对象
- 没有释放机制(对象不会返回给提供者)
- 不得泄漏(未使用的对象有资格进行垃圾回收)
这可以通过使用以下types的内部存储映射来实现:
WeakHashMap<Mutex, WeakReference<Mutex>>
该对象是关键和价值。 当地图外部没有任何对象的硬引用时,它可以被垃圾收集。 映射中的值与硬引用一起存储,所以必须将值包装在WeakReference中以防止内存泄漏。 最后一点在javadoc中介绍 。
我做了一个谷歌代码search“新的WeakHashMap()”。
我从GNU classpath项目中获得了一堆匹配
- Apache的xbean项目: WeakHashMapEditor.java
- Apache Lucene项目: CachingWrapperFilter.java
如上所述,只要存在强有力的参照,就会有弱的参照。
一个示例用法是在侦听器中使用WeakReference,以便一旦对其目标对象的主引用消失,侦听器就不再处于活动状态。 请注意,这并不意味着WeakReference已从侦听器列表中删除,清理仍然是必需的,但可以在预定的时间执行。 这也具有防止被监听对象保持强引用并最终成为内存膨胀源的效果。 示例:Swing GUI组件引用具有比窗口更长生命周期的模型。
在如上所述与听众一起玩的同时,我们很快意识到,从用户的angular度来看,“立即”收集对象。
您可以使用weakhashmap为扩展对象创build实现无资源caching。
但是请注意,不可变的对象。 我用它来caching查询结果(这需要大约400毫秒执行)到一个很less更新的文本search引擎。