弱引用的使用是我从未见过的实现,因此我正在尝试找出它们的用例是什么,以及实现如何工作。你什么时候需要使用WeakHashMap或WeakReference,它是如何使用的?
- 相关问题有好答案:为什么我们需要Java中的弱引用,而Java中的软引用和弱引用有什么区别呢?
- 类似:使用weakhashmap?
One problem with strong references is
caching, particular with very large
structures like images. Suppose you
have an application which has to work
with user-supplied images, like the
web site design tool I work on.
Naturally you want to cache these
images, because loading them from disk
is very expensive and you want to
avoid the possibility of having two
copies of the (potentially gigantic)
image in memory at once.
Because an image cache is supposed to
prevent us from reloading images when
we don't absolutely need to, you will
quickly realize that the cache should
always contain a reference to any
image which is already in memory. With
ordinary strong references, though,
that reference itself will force the
image to remain in memory, which
requires you to somehow determine when
the image is no longer needed in
memory and remove it from the cache,
so that it becomes eligible for
garbage collection. You are forced to
duplicate the behavior of the garbage
collector and manually determine
whether or not an object should be in
memory.
了解弱参考,伊桑·尼古拉斯
- 在这种情况下,软引用不会更好,即只在内存开始耗尽时收集的引用。
- 这篇文章也是如此,请阅读其余部分。
- 我有点困惑…假设我有一个SWT图像缓存。SWT映像需要通过dispose()方法释放SO资源。如果我使用weakhashmap来存储它们,则确切地显示gc将处理对象吗?
- @Marcolopes GC将在任何其他对象上使用终结器。看起来SWT不喜欢这样做,所以我认为你不能用WeakHashMap来管理操作系统资源。
- @Marcolopes,(我假设您的GC保证在回收内存之前调用Finalize。)如果在缓存终结器中完成了Dispose,那么一切都很好。如果必须手动调用Dispose,则可以1)扩展类并将Dispose放入终结器,或者2)使用PhantomReference跟踪并相应地运行Dispose。选项2更好(避免了复活错误,并使您能够在另一个线程上运行Dispose),但是选项1在没有helperClasses的情况下更容易实现。
- 文章现在在Web.Goivi.org/Web/20061130103858/http://WebLogs.java. .NET/B& ZWNJ;&8203;Log//Helip;
要清楚的一个区别是WeakReference和SoftReference之间的区别。
基本上,只要被引用对象没有硬引用,JVM就会急切地使用一个WeakReference。另一方面,在垃圾收集器真正需要回收内存之前,SoftReferenced对象往往会被留下来。
值保存在WeakReference中的缓存将非常无用(在WeakHashMap中,它是弱引用的键)。当您想要实现一个缓存时,SoftReferences非常有用,该缓存可以随着可用内存的增长和收缩而增长和收缩。
- "在weakreferences中保存值的缓存将非常无用"我完全不同意。
- @特立尼奥-呃,我真的不知道该说什么。为什么缓存的值在您不引用它们的时候就消失了,这正是一件有用的事情?
- 对于类似memoization的内容,可以使用一个松散地保存缓存值的缓存。
- @托马斯丁,我还是不明白。缓存似乎唯一有用的时间是没有其他引用时…如果您有对它的引用,您需要一个缓存来做什么?
- 另请参见stackoverflow.com/a/1803213/632951了解SoftHashMap。
- @SoftRef告诉环境"存储这个直到你没有内存"。weakref告诉环境"在gc运行之前保存它"。坦率地说,weakref没有任何用例,除非您正在调试/分析GC本身。如果需要内存敏感的缓存,请使用SoftRef。如果你不想要缓存,那就不要缓存它!韦克里夫从哪里来?
- 另一方面,JVM可能不会将weakref视为"在GC运行之前存储它",而是"在没有内存之前存储它,考虑到内存SoftRef的需要"。在这样一个JVM中,weakref只是一个软引用级别2。换句话说,这是一种让这样一个JVM为您进行SoftRef调平的黑客方式,这样您就不必自己实现SoftRef调平代码。但是,一旦您需要超过2个级别的SoftRef,您仍然需要自己编写代码。
- @Pacrier:我不知道存在软引用。它们似乎更有用。
- @cruncher"如果你有对它的引用,你需要缓存做什么?"所以代码中的其他地方可以很容易地查找相同的内容。假设我在磁盘上有一个图像,一个可能在几个不同的粒子系统中使用,这些系统彼此不了解……联轴器松动。但是,例如,您可以根据这些图像的文件路径将它们保存在缓存中。
- @Pacerier:我仍然不认为weakhashmap作为缓存有问题。开导我。Java缺乏C++析构函数和刚性对象寿命控制(一个特性,它消除了一类错误,内存泄漏(或至少使它们更稀少))…但这会使清理GC不直接处理的东西变得容易出错。基于弱/软引用的缓存似乎也消除了一类(小得多)错误。问题出在哪里?
- 作为一个缓存,WeakHashMap没有什么问题——谁说有问题?
WeakReferences和WeakHashMaps的一个常见用途是向对象添加属性。有时,您希望向对象添加一些功能或数据,但子类化和/或组合不是一个选项。在这种情况下,最明显的做法是创建一个哈希映射,将要扩展到要添加的属性的对象链接起来。然后,只要你需要这个财产,你就可以在地图上查到它。但是,如果要添加属性的对象往往会被销毁并创建很多,那么最终可能会在地图中有许多旧对象占用大量内存。
如果您使用WeakHashMap代替,对象一旦不再被程序的其余部分使用,它们就会离开您的映射,这是所需的行为。
我必须这样做才能向java.awt.Component中添加一些数据,以避免jre在1.4.2和1.5之间发生变化,我可以通过对感兴趣的每个组件(JButton、JFrame、JPanel)进行子类化来修复它,但使用更少的代码就容易得多。
- weakhashmap如何知道"它们不再被你程序的其他部分使用"?
- 弱Hasmap使用弱引用作为其键。当一个对象只通过弱引用被引用时,垃圾收集器将"通知"弱引用的所有者(在本例中是weahashmap)。我将阅读javadocs中的weakreferences和referencequeues,了解这些对象如何与垃圾收集器交互。
- 谢谢卢克,你能为你提供简单的代码吗?
- 因此,弱引用仅在Java中是有意义的,因为Java的古怪API在某些类无法扩展。
对于WeakHashMap和WeakReference,另一个有用的例子是侦听器注册表实现。
当你创建了一些想要听某些事件的东西时,通常你注册一个监听器,例如。
1
| manager.registerListener(myListenerImpl); |
如果manager将侦听器存储在WeakReference中,这意味着您不需要删除寄存器,例如使用manager.removeListener(myListenerImpl)中的寄存器,因为一旦侦听器或保存侦听器的组件不可用,它将自动删除。
当然,您仍然可以手动删除您的侦听器,但是如果您不删除或忘记它,它不会导致内存泄漏,也不会阻止您的侦听器被垃圾收集。
WeakHashMap是在哪里出现的?
希望将已注册的侦听器存储为WeakReferences的侦听器注册表需要一个集合来存储这些引用。在标准Java库中没有EDOCX1 14实现,只有EDOCX1×1,但是我们可以很容易地使用后者来实现第一个功能:
1 2
| Set <ListenerType > listenerSet =
Collections. newSetFromMap(new WeakHashMap <ListenerType, Boolean >()); |
使用这个listenerSet来注册一个新的侦听器,您只需将它添加到集合中,即使它没有被显式删除,如果侦听器不再被引用,它也将被JVM自动删除。
- 对侦听器列表使用weakhashset的问题是,在register()中创建的匿名侦听器实例很容易丢失,这是用户不可预料的。相反,更安全的做法是从管理者那里获得对听众更有力的引用,并依靠呼叫者来做正确的事情。
- 对于侦听器注册表实现:所有已注册的侦听器将在下一次GC触发时被收集/销毁?例如,当激发所有侦听器的onSomethingOccured()方法时,如果GC被踢了会发生什么?
- @伊卡,我不敢相信人们还在兜售这个神话。这是一个完全错误的答案。你也可以说,对于WeakHashMap来说,另一个有用的例子是每当你需要一个HashMap对象时。所以,哇哦,你不必手动执行hashmap.remove,因为一旦obj超出范围,项目就会自动删除!真的很神奇!这样一个丑陋的魔术黑客是一个完整的掌心。
- @Pacerier:我一直在这里关注着Javascript Land中其他注释的链接,仍然不太明白为什么使用weakmap实现侦听器注册表是一个神话。例如,如果WebSocket客户机应该通过注册表服务与它们上的某些侦听器绑定,那么将套接字对象存储为weakmap中的一个键(以防止它们在连接关闭后挂起在内存中,例如,出错时),并能够在需要时检索所有侦听器似乎是合乎逻辑的。那么,你能说明一下,这种方法到底有什么问题吗?
- @我也不明白你的反对意见。在发布订阅或事件总线场景中,弱引用集合对我来说非常有意义。订阅对象允许超出范围并直接进入垃圾收集,而无需正式取消订阅。如果第三方对象在不知道订阅对象的情况下负责初始订阅,则取消订阅的过程可能特别复杂。WeakReference的集合大大简化了代码库,并避免了与未能取消订阅相关的不必要的错误。有什么不利因素?
- @如果仍然存在,那么在调用它之前,Pacerier会如何创建一个对异常或缺失的观察者的强引用呢?它应该修复在勾选或类似情况后立即调用它们的错误。
这篇博客文章演示了这两个类的用法:Java:在ID上同步。用法如下:
1 2 3 4 5 6 7 8
| 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返回不同的对象
- 没有释放机制(对象不会返回到提供程序)
- 不能泄漏(未使用的对象可以进行垃圾收集)
这是通过使用类型为的内部存储映射实现的:
1
| WeakHashMap<Mutex, WeakReference<Mutex>> |
对象既是键又是值。当映射外部没有对象的硬引用时,可以对其进行垃圾收集。映射中的值存储在硬引用中,因此必须将该值包装在weakreference中以防止内存泄漏。最后一点在javadoc中介绍。
如上所述,只要存在强引用,弱引用就被保留。
一个示例用法是在监听器内部使用weakreference,这样监听器在其目标对象的主引用消失后就不再活动。注意,这并不意味着weakreference会从监听器列表中删除,清理仍然是必需的,但可以在预定的时间执行。这也有防止被监听对象持有强引用并最终成为内存膨胀源的效果。示例:Swing GUI组件引用的模型的生命周期比窗口长。
当我们像上面所描述的那样和听众一起玩的时候,我们很快意识到,从用户的角度来看,对象被"立即"收集。
- 谢谢有用的回答。但我想知道,在这种情况下,是否应该强烈地注册(引用)侦听器?
- 这个答案完全错误。详细说明:stackoverflow.com/questions/154724/&hellip;
- @Pacerier-对于WeakReferences,你的评论完全错误!
例如,如果您希望跟踪从某个类创建的所有对象。为了仍然允许对这些对象进行垃圾收集,可以保留对对象的弱引用的列表/映射,而不是对象本身。
现在如果有人能解释幽灵对我的引用,我会很高兴…
- 一种用法:phantomreferences允许您精确地确定从内存中删除对象的时间。事实上,它们是唯一确定这一点的方法。(WebLogs.java.NET/BLOG/EnICHOLAS/存档/ 2006/05/& Helip;
- 事实上,除非你明确地清除它,否则它不会被删除。"与软引用和弱引用不同,虚引用在排队时不会被垃圾收集器自动清除。通过幻象引用可访问的对象将保持不变,直到清除所有此类引用或使其自身无法访问为止。"
- @琼特洛,但它已经完成,所有成员都走了。实际上,这是一个空白的对象。见stackoverflow.com/q/7048767/632951
我在Weakreferences中的一个实际用途是,如果你有一个非常大的,很少使用的物体。您不希望在不需要的时候将它保存在内存中;但是,如果另一个线程需要相同的对象,您也不希望在内存中有两个对象。可以在某个地方保留对对象的弱引用,在使用它的方法中保留硬引用;当两个方法都完成时,将收集对象。
- 这是一个软引用,而不是一个weakreference。见stackoverflow.com/a/155492/632951
我在谷歌代码中搜索了"new weakhashmap()"。
我从GNU类路径项目中得到了一堆匹配项,
Apache Xbean项目:weakhashmapeditor.java
Apache Lucene项目:cachingWrapperFilter.java
您可以使用weakhashmap实现一个用于扩展对象创建的无资源缓存。
但请注意,不希望有可变对象。我用它将查询结果(执行大约需要400毫秒)缓存到一个文本搜索引擎中,这个引擎很少更新。
- 你说的是一个软引用,而不是一个weakreference。见stackoverflow.com/a/155492/632951