何时使用 WeakHashMap 或 WeakReference?

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

60500 次浏览

例如,如果您想跟踪某个类创建的所有对象。为了仍然允许对这些对象进行垃圾收集,需要保留对对象的弱引用列表/映射,而不是对象本身。

现在如果有人能给我解释一下“幽灵”这个词,我会很高兴..。

强引用的一个问题是 缓存,特别是对于非常大的 像图像一样的结构。假设你 有一个应用程序必须工作 用户提供的图像,如 网站设计工具,我的工作。 当然你想要缓存这些 映像,因为从磁盘加载它们 是非常昂贵的,你想 避免生两个孩子的可能性 (潜在的巨大的)副本 立刻在记忆中形象化。

因为图像缓存应该 阻止我们重新载入图像时 我们不需要,你会的 很快意识到缓存应该 始终包含对任何 已经存储在内存中的图像 普通的强烈参考,虽然, 引用本身将强制 图像保留在记忆,其中 需要你设法确定什么时候 图像不再需要 存储器并从缓存中删除它, 这样它就有资格获得 垃圾收集。你被迫 复制垃圾的行为 收集器和手动确定 一个对象是否应该在 记忆。

理解弱引用 ,Ethan Nicholas

我对 WeakReferences 的一个实际应用是,如果您有一个很少使用的单个非常大的对象。当不需要它时,您不希望将它保存在内存中; 但是,如果另一个线程需要相同的对象,您也不希望其中两个对象保存在内存中。您可以在某个地方保留对对象的弱引用,并在使用它的方法中保留硬引用; 当两个方法都完成时,将收集该对象。

您可以使用弱散列映射来实现无资源缓存以创建可扩展对象。

但请注意,不希望有可变对象。 我使用它来缓存查询结果(执行大约需要400毫秒)到一个文本搜索引擎,这是很少更新。

我用谷歌代码搜索了“ new WeakHashMap ()”。

我从 GNU 类路径项目中找到了一些匹配的

  1. Apache xbean 项目: Java
  2. Apache Lucene 项目: 缓存 WrapperFilter.java

这篇博文演示了这两个类的用法: 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 返回不同的对象
  • 没有释放机制(对象不返回给提供程序)
  • 不能泄漏(未使用的对象有资格进行垃圾收集)

这是通过使用类型为:

WeakHashMap<Mutex, WeakReference<Mutex>>

对象既是键又是值。当映射外部没有对对象的硬引用时,可以对其进行垃圾收集。映射中的值存储在硬引用中,因此必须将该值封装在 弱参考中以防止内存泄漏。最后一点在 Javadoc中有所介绍。

WeakReferenceSoftReference

一个需要明确的区别是 WeakReferenceSoftReference之间的区别。

基本上,一旦被引用的对象没有对它的 用力引用,JVM将急切地把 WeakReference变成 GC-D。另一方面,SoftReferenced 对象在真正需要回收内存之前,往往会被垃圾收集器遗留下来。

WeakReference中保存 价值观的缓存将是非常无用的(在 WeakHashMap中,它是弱引用的键)。当您希望实现一个可以随着可用内存的增长和缩小而增长的缓存时,SoftReferences对于包装这些值非常有用。

如上所述,弱参考只要强参考存在,就持有弱参考。

示例用法是在侦听器内部使用 WeakReference,这样一旦对目标对象的主引用消失,侦听器就不再处于活动状态。 请注意,这并不意味着 WeakReference 从侦听器列表中删除,仍然需要进行清理,但是可以在预定的时间执行。 这还可以防止被监听的对象持有强引用,并最终成为内存膨胀的来源。 示例: Swing GUI 组件引用比窗口具有更长生命周期的模型。

当我们像上面描述的那样与听众玩游戏时,我们很快意识到对象会“立即”从用户的角度被收集起来。

特别是 WeakReferenceWeakHashMap的一个常见用法是向对象添加属性。有时候你想给一个对象添加一些功能或数据,但是子类化和/或组合不是一个选项,在这种情况下,显而易见要做的事情是创建一个散列表,将你想要扩展的对象链接到你想要添加的属性。然后当你需要的财产,你可以只是在地图上查找它。但是,如果您要添加属性的对象倾向于被大量破坏和创建,那么最终可能会导致映射中的大量旧对象占用大量内存。

如果使用 WeakHashMap代替,那么一旦程序的其他部分不再使用这些对象,它们就会立即离开映射,这是所期望的行为。

我不得不这样做,以便向 java.awt.Component添加一些数据来绕过 JRE 在1.4.2和1.5之间的变化,我可以通过子类化我感兴趣的每个组件(JButtonJFrameJPanel...)来解决这个问题,但是用更少的代码就更容易了。

WeakHashMapWeakReference的另一个有用的例子是 侦听器注册表实现

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

manager.registerListener(myListenerImpl);

如果 managerWeakReference存储侦听器,这意味着您不需要删除寄存器,例如用 manager.removeListener(myListenerImpl),因为一旦您的侦听器或保存侦听器的组件变得不可用,它将被自动删除。

当然,您仍然可以手动删除您的侦听器,但是,如果您没有或者您忘记了它,它不会导致内存泄漏,也不会阻止您的侦听器被垃圾收集。

WeakHashMap从何而来?

希望将已注册的侦听器存储为 WeakReference的侦听器注册表需要一个集合来存储这些引用。在标准 Java 库中没有 WeakHashSet实现,只有一个 WeakHashMap,但我们可以很容易地使用后一个来“实现”第一个的功能:

Set<ListenerType> listenerSet =
Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

使用这个 listenerSet注册一个新的侦听器,您只需要将它添加到集合中,即使没有显式地删除它,如果不再引用侦听器,它也会被 JVM 自动删除。