15

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

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

4

4 回答 4

20

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

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

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

短语“不再正常使用”实际上意味着关键对象不再是强或软可达的;即通过一系列强和/或软引用。

数据结构如何知道我将不再在程序中使用密钥?

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

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

唯一的参与WeakHashmap是:

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

如果我长时间不参考密钥怎么办?

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

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


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

于 2012-06-24T00:19:22.577 回答
4

JVM 垃圾按以下顺序收集:

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

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

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

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

来自 SoftReference 的 javadoc:

所有对软可访问对象的软引用都保证在虚拟机抛出一个OutOfMemoryError

于 2012-06-24T00:02:50.250 回答
4

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

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

希望这可以帮助!

于 2012-06-23T23:29:55.610 回答
2

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

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

于 2012-06-24T00:10:54.420 回答