【问题标题】:How does a weak hash map know to garbage-collect an object?弱哈希映射如何知道垃圾收集对象?
【发布时间】:2012-06-23 23:27:55
【问题描述】:

我最近发现了 Java 中的 WeakHashMap 数据结构。

但是,我不明白当映射不再正常使用时垃圾收集映射是什么意思。数据结构如何知道我将不再在程序中使用密钥?如果我长时间不引用密钥怎么办?

【问题讨论】:

  • 有关何时使用这些Weak 数据结构的详细信息,请参阅this question
  • WeakHashMap 不“知道垃圾收集”任何东西。垃圾收集器会。 WeakHashMap 添加的只是弱引用和引用队列的使用。

标签: java data-structures garbage-collection weakhashmap


【解决方案1】:

但是,我不明白当映射不再正常使用时垃圾收集映射是什么意思。

好的。在正常情况下,当垃圾收集器运行时,它会删除您的程序不能再使用的对象。技术术语是“无法访问的对象”,它意味着程序执行无法再获取对该对象的引用。一个无法访问的对象,可能在下一个 GC 周期中被收集...或不。无论哪种方式,它都不再是应用程序的关注点。

在这种情况下,WeakHashMap 使用一个名为 WeakReference 的特殊类来引用键1。弱引用是一个对象,其行为有点像间接指针(指向持有指针的对象的指针)。它有一个有趣的特性,即允许垃圾收集器破坏引用;即将它包含的引用替换为null。规则是,当 GC 注意到对象不再可通过正常(强)或软引用链访问时,对对象的弱引用将被破坏2.

短语“不再普通使用”真正的意思是关键对象不再是强或软可达的;即通过一系列强引用和/或软引用。

数据结构如何知道我将不再在我的程序中使用某个键?

WeakHashmap 不这样做。相反,是 GC 注意到密钥不是强可达的。

作为其正常遍历的一部分,GC 将找到并标记所有强可达对象。然后它遍历所有WeakReference 对象并检查是否 他们所指的对象已被标记,如果没有,则将其破坏。 (或者类似的东西......我从未看过实际的 GC 实现。而且它还必须处理 SoftReferencePhantomReference 对象这一事实使情况变得复杂。)

WeakHashmap 唯一的参与是:

  • 它创建并使用 WeakReference 对象作为键,并且
  • 它会删除键 WeakReferences 已被 GC 清除的哈希表条目。

如果我长时间不引用某个键怎么办?

决定弱引用应该被打破的标准不是基于时间的。

但是时间可能会影响是否删除键。例如,一个键可以 1) 不再是强引用,2) 从映射中检索,3) 分配给一个可达变量,使其再次被强引用。如果 GC 在 key 不可强可达的窗口期间没有运行,则 key 及其关联的 value 将保留在 map 中。 (这就是你想要发生的事情......)


1 - 实现细节:在最近的 Java 版本中,弱引用实际上是指映射的内部 Entry 对象而不是键。这允许更有效地从地图中清除损坏的引用。详情请看代码。
2 - 软引用是一种在堆内存不足时允许 GC 中断的引用。

【讨论】:

    【解决方案2】:

    Java 有一个 references 系统,该语言可以告诉您的代码某个对象是否仍在使用中。您可以使用引用来检测某些对象何时被明确标识为不再使用或不可用,然后可以采取相应的措施。 This tutorial 深入介绍了参考资料,以防您对如何使用它们感到好奇。

    在内部,WeakHashMap 可能会使用这些引用来自动检测何时无法再使用给定的密钥。然后,实现可以从哈希表中删除这些对象,以便它们不再占用任何空间。

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

      JVM 垃圾按此顺序收集:

      1. 未引用的对象
      2. 唯一引用为WeakReference 的对象
      3. 唯一引用为SoftReference 的对象

      通常,垃圾收集器只会对未引用的对象进行垃圾收集。

      就垃圾收集器而言,对对象的弱引用不算作引用。垃圾收集器可能会也可能不会收集它们。通常,除非内存不足,否则它不会收集它们,但不能保证。

      如果 JVM 即将耗尽内存,垃圾收集器将收集软引用对象。所有弱引用对象都将被垃圾回收任何软引用对象被垃圾回收之前。

      来自 SoftReference 的 javadoc:

      保证在虚拟机抛出OutOfMemoryError之前清除所有对软可访问对象的软引用

      【讨论】:

        【解决方案4】:

        我读到您的问题是询问“常规使用”的具体措辞,因此假设您已经了解强引用和弱引用。常规使用是指弱哈希映射包含的键也被其他一些数据结构(强)引用的情况。至少存在一个对密钥的强引用是“常规使用”。只要这个其他数据结构引用它,就不能对密钥进行垃圾收集。当另一个数据结构不再可达(指向它的指针不再存在)时,键也变得不可达。它不再经常使用,因为对它的唯一引用是映射中的弱引用。垃圾收集器最终可以回收它,并且映射消失了。

        当您想通过子类化来扩展类型 C 但不能:例如当 C 是具有许多实现的接口时,就会出现这种情况。您可以通过使用具有键类型 C 的弱哈希映射和要添加的新字段包装在新类 E 中来解决此问题。每当您创建 C 实例时,您还创建一个 E 实例并将该对添加到映射中,这然后用于在 C 实例的生命周期内访问新字段。当 C 实例变成垃圾时,映射和 E 实例也一样。这是自动的,因为 hashmap 很弱。如果不是,则必须以与在没有垃圾收集器的语言中显式释放存储相同的方式手动清理它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-08
          • 2012-10-04
          • 1970-01-01
          • 2011-07-16
          • 2011-01-04
          相关资源
          最近更新 更多