【问题标题】:WeakHashMap and strongly referenced valueWeakHashMap 和强引用值
【发布时间】:2012-02-28 07:28:39
【问题描述】:

Javadocs 说“当一个键被丢弃时,它的条目有效地从地图中删除”。

但是除非有另一个线程偶尔删除这样的Map.Entry 条目,否则值对象不会被映射强引用吗?但由于没有这样的线程在运行,只有 get 方法调用可以删除这样的条目 - 一次一个。

出于这个原因,我几乎总是使用WeakHashMap<K, WeakReference<V>>。为什么他们不将其作为默认行为 - 也将值作为弱引用?

【问题讨论】:

  • stackoverflow.com/questions/2473410/question-about-weakhashmap 几乎是一个相同的问题。但我想知道我的断言是否正确:只有当 get() 发现密钥已被 gc'ed 时才会删除条目,坦率地说,除非我将 WeakReferences 用作值,否则这似乎没有多大价值。
  • 是的,它只是轮询。如果你想要不同的东西,写你自己的版本。
  • 如果您需要不同的东西,请使用库——编写自己的库非常棘手。 Guava 有MapMaker,它可以让你配置键和值引用的强度:docs.guava-libraries.googlecode.com/git-history/release/javadoc/…
  • 来自ibm.com/developerworks/java/library/j-jtp11225 我认为 WeakHashMap 是半生不熟的,为什么他们使用 expungeStaleEntries() 作为大多数方法的一部分来轮询队列并且没有专门的线程来阻塞读取?跨度>
  • @Ustaman Sangat WeakHashMap 投票ReferenceQueue

标签: java collections weak-references weakhashmap


【解决方案1】:

引用队列用于自动删除条目。

http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/ref/ReferenceQueue.html

引用队列,在检测到适当的可达性更改后,垃圾收集器会将注册的引用对象附加到其中。

基本上,弱引用是垃圾收集器的核心部分,因此当发生 GC 扫描时,会找到未使用的引用并将其放入队列中,然后可以根据这些队列的内容采取行动。

线程可以位于队列的remove 方法上,以便在需要进行清理或poll 队列时收到警报。

"Java theory and practice: Plugging memory leaks with weak references" 解释:

WeakHashMap 的实现说明了一个带有弱引用的常见习惯用法——即某些内部对象扩展了WeakReference

...

WeakHashMap 使用弱引用来保存映射键,这允许在应用程序不再使用键对象时对其进行垃圾回收,get() 实现可以通过是否WeakReference.get() 返回null。但这只是在应用程序的整个生命周期中保持 Map 的内存消耗不增加所需的一半;在收集到关键对象后,还必须做一些事情来从 Map 中删除死条目。 否则,Map 将简单地填充与死键对应的条目。虽然这对应用程序不可见,但仍可能导致应用程序耗尽内存,因为 Map.Entry 和 value即使键是,也不会收集对象。

...

引用队列是垃圾收集器向应用程序反馈对象生命周期信息的主要方式。 弱引用有两个构造函数:一个仅将引用对象作为参数,另一个也接受引用队列。当使用关联的引用队列创建弱引用并且引用对象成为 GC 的候选对象时,引用对象(不是引用对象)在引用被清除后排队到引用队列中。然后,应用程序可以从引用队列中检索引用并获悉该引用已被收集,因此它可以执行相关的清理活动,例如清除已从弱集合中删除的对象的条目。 (引用队列提供与 BlockingQueue 相同的出队模式——轮询、定时阻塞和不定时阻塞。)

编辑:

即使有队列,weak map 仍然会泄漏。 Ephemerons 试图解决弱键引用引用该键的强持有值的情况。它们不能在 java 中实现。

Ephemerons 解决了在尝试使用注册表将属性“附加”到对象时常见的问题。当某个属性应该附加到一个对象时,该属性应该(就 GC 行为而言)通常具有该对象的实例变量所具有的生命周期。但是,由于对象与其属性之间存在外部关联,这会变得复杂,例如:

property --------- registry --------- association --------- object

在这里,注册表(第三方)将保留关联本身,这需要手动从注册表中删除(而不是自动垃圾收集)。虽然这个问题总是可以在任何给定的具体情况下通过使用各种弱关联类型之一来解决,但选择“正确”的关联类型取决于多种因素,其中一些因素可以动态变化。

Ephemeron 解决了这个问题,方法是定义一个 ephemeron 的“内容”(值)将被强保留,直到已知密钥被垃圾回收。从那时起,蜉蝣的内容将被弱持有。因此,当且仅当键是可垃圾回收的,即我们将观察到的对象实例变量的确切行为时,ephemeron 的内容才有资格进行垃圾回收。

【讨论】:

  • 忘记了引用队列调用我们自己的钩子的能力。
  • 我从您的链接中发现“WeakHashMap 有一个名为 expungeStaleEntries() 的私有方法,在大多数 Map 操作期间都会调用该方法。”有用。并且想知道是否产生一个实际上会在引用队列上进行阻塞读取的线程可能是一个更好的主意。也许这就是 Guava 的 MapMaker 所做的。
  • @UstamanSangat,我不知道 MapMaker 做了什么,但也许 expungeStaleEntries() 会轮询参考队列以使用地图分期清理。在实施我自己的密钥清理之前,我会尝试使用 MapMaker 之类的东西。顺便说一句,我添加了一个编辑,指出即使使用弱键映射也会泄漏内存。
  • 我认为 ephemeron 是一个概念,但您似乎在 JVM 上提出了一些具体的建议?
  • @UstamanSangat,我的理解是,通常必须将 ephemerons 设计到内存语义中。下一版本的 EcmaScript 将拥有它们。普通 JVM 没有。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 2014-05-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多