你什么时候使用WeakHashMap或WeakReference?

18R*_*bit 161 java weak-references

使用弱引用是我从未见过的实现,所以我试图弄清楚它们的用例是什么以及实现如何工作.什么时候需要使用WeakHashMap或者WeakReference它是如何使用的?

Jac*_*all 96

强引用的一个问题是缓存,特别是对于像图像这样的非常大的结构.假设您有一个必须使用用户提供的图像的应用程序,例如我工作的网站设计工具.当然,您希望缓存这些图像,因为从磁盘加载它们非常昂贵,并且您希望避免在内存中同时存储两个(可能是巨大的)图像副本的可能性.

因为当我们不绝对需要时,图像缓存应该阻止我们重新加载图像,你会很快意识到缓存应该总是包含对已经在内存中的任何图像的引用.但是,对于普通的强引用,引用本身会强制图像保留在内存中,这需要您以某种方式确定内存中何时不再需要该图像并将其从缓存中删除,以使其符合垃圾回收的条件.您被迫复制垃圾收集器的行为并手动确定对象是否应该在内存中.

理解弱参考,Ethan Nicholas

  • 在这种情况下,SoftReferences不会更好,即仅在内存开始耗尽时收集的引用. (42认同)
  • 这篇文章也是如此,请阅读其余内容. (3认同)
  • @marcolopes GC会在任何其他对象上使用终结器.当你这样做时,SWT似乎不喜欢它,所以我认为你不能用`WeakHashMap`来管理操作系统资源. (2认同)
  • 文章现在在 https://web.archive.org/web/20061130103858/http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html (2认同)

oxb*_*kes 54

要明确的一个区别是a WeakReference和a 之间的区别SoftReference.

基本上WeakReference,一旦被引用的对象没有引用,JVM就会急切地想要GC-d .一个SoftReference在另一方面d对象,往往会被垃圾收集器留约,直到它真正需要回收内存.

保存在WeakReferences中的缓存将是非常无用的(在a中SoftReference,它是弱引用的键).WeakReference当您想要实现可以使用可用内存增长和缩小的缓存时,可以将值包装起来

  • @trithithis - 嗯,我真的不知道该说些什么.为什么一个缓存的值会在你没有引用它们的时候消失*一个有用的东西*,确切地说? (5认同)
  • @ThomasEding我仍然没有得到它.缓存似乎有用的唯一时间是没有其他引用...如果你有引用它,你需要什么缓存? (5认同)
  • "在WeakReferences中保存值的缓存将是非常无用的"我完全不同意. (4认同)
  • 对于像memoization这样的东西,松散地保存其缓存值的缓存可能很有用. (4认同)
  • @ThomasEding,Softref告诉环境"存储这个直到你没有记忆".Weakref告诉环境"存储这个直到GC运行".除非您正在调试/分析GC本身,否则坦率地说没有用于weakref的用例.如果需要内存敏感的缓存,请使用softref.**如果您不想要缓存,请不要缓存它!**weakref在哪里进来? (2认同)

luk*_*uke 30

WeakReferences和WeakHashMaps的一个常见用途是用于向对象添加属性.有时您希望向对象添加一些功能或数据,但子类和/或组合不是一个选项,在这种情况下,显而易见的事情是创建一个hashmap,将您想要扩展的对象链接到要添加的属性.然后,无论何时您需要房产,您都可以在地图上查找.但是,如果要添加属性的对象倾向于被破坏并且被创建很多,则最终会导致地图中的许多旧对象占用大量内存.

如果您使用的WeakHashMap是对象,只要程序的其余部分不再使用它们就会离开您的地图,这是所需的行为.

我不得不这样做是为了增加一些数据,以java.awt.Component避开在1.4.2和1.5之间的JRE的变化,我可以通过继承每个组件我很感兴趣,INT(固定它JButton,JFrame,JPanel...),但是,这是多少使用更少的代码更容易.

  • 弱hasmap使用弱引用作为其键.当一个对象只通过弱引用引用时,垃圾收集器将"通知"弱引用的所有者(在本例中为WeaHashMap).我将在javadocs中阅读WeakReferences和ReferenceQueues,以了解这些对象如何与垃圾收集器交互. (2认同)

icz*_*cza 20

对于另一个有用的情况WeakHashMapWeakReference是一个听众注册表实现.

当你创建想要听某些事件的东西时,通常你会注册一个监听器,例如

manager.registerListener(myListenerImpl);
Run Code Online (Sandbox Code Playgroud)

如果manager将监听器存储为a WeakReference,则表示您不需要删除寄存器,例如a,manager.removeListener(myListenerImpl)因为一旦您的监听器或持有监听器的组件变得不可用,它将自动删除.

当然你仍然可以手动删除你的监听器,但是如果你没有或你忘了它,它不会导致内存泄漏,并且它不会阻止你的监听器被垃圾收集.

WeakHashMap图片在哪里?

用于存储已注册的侦听器的侦听器注册表WeakReference需要一个集合来存储这些引用.WeakHashSet标准Java库中没有实现,WeakHashMap但我们可以轻松地使用后者来"实现"第一个的功能:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());
Run Code Online (Sandbox Code Playgroud)

使用此listenerSet命令注册一个新的侦听器,您只需将其添加到集合中,即使未显式删除它,如果不再引用侦听器,它也将由JVM自动删除.

  • 将weakHashSets用于侦听器列表的问题是在register()中创建的匿名侦听器实例将很容易丢失,这将是用户意外的.相反,从管理者那里获得对听众的更强引用更安全,并依赖于调用者来做正确的事情. (10认同)
  • @Pacerier:我已经跟着你在JavaScript中的其他评论的链接一直到这里,但仍然不太明白为什么监听器注册表实现与WeakMap是一个神话.例如,如果WebSocket客户端应该通过注册表服务绑定到它们上的某些侦听器,那么将套接字对象存储为WeakMap中的键(以防止它们在连接关闭后挂起,比如说出错)并且能够如果需要,检索所有听众.那么,请你说明一下,这种方法到底是什么问题? (3认同)
  • @Pacerier 我也无法理解你的反对意见。在发布-订阅或事件总线场景中,弱引用的集合对我来说非常有意义。订阅对象可以超出范围并进入垃圾回收,而无需正式取消订阅。如果第三方对象在订阅对象不知情的情况下负责初始订阅,则取消订阅的过程可能会特别复杂。“WeakReference”的集合极大地简化了代码库,并避免了与取消订阅失败相关的不必要的错误。有什么缺点? (2认同)

McD*_*ell 5

这篇博文演示了这两个类的使用: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
    }
}
Run Code Online (Sandbox Code Playgroud)

IdMutextProvider 提供基于 id 的对象进行同步。要求是:

  • 必须返回对同一对象的引用以同时使用等效 ID
  • 必须为不同的 ID 返回不同的对象
  • 没有释放机制(对象不会返回给提供者)
  • 不得泄漏(未使用的对象有资格进行垃圾收集)

这是使用以下类型的内部存储映射来实现的:

WeakHashMap<Mutex, WeakReference<Mutex>>
Run Code Online (Sandbox Code Playgroud)

对象既是键又是值。当地图外部的任何东西都没有对对象的硬引用时,它可以被垃圾收集。映射中的值使用硬引用存储,因此必须将该值包装在WeakReference 中以防止内存泄漏。最后一点在javadoc 中有介绍