我最近发现了WeakHashMap
Java 中的数据结构。
但是,我不明白当映射不再正常使用时垃圾收集映射是什么意思。数据结构如何知道我将不再在程序中使用密钥?如果我长时间不参考密钥怎么办?
我最近发现了WeakHashMap
Java 中的数据结构。
但是,我不明白当映射不再正常使用时垃圾收集映射是什么意思。数据结构如何知道我将不再在程序中使用密钥?如果我长时间不参考密钥怎么办?
但是,我不明白当映射不再正常使用时垃圾收集映射是什么意思。
好的。在正常情况下,当垃圾收集器运行时,它会移除你的程序不能再使用的对象。技术术语是“无法访问的对象”,它意味着程序执行无法再获取对该对象的引用。一个无法访问的对象,可能会在下一个 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 中断的引用。
JVM 垃圾按以下顺序收集:
WeakReference
SoftReference
通常,垃圾收集器只对未引用的对象进行垃圾收集。
就垃圾收集器而言,对对象的弱引用不算作引用。垃圾收集器可能会也可能不会收集它们。通常,除非内存不足,否则它不会收集它们,但不能保证。
如果 JVM 即将耗尽内存,垃圾收集器将收集软引用对象。在任何软引用对象被垃圾收集之前,所有弱引用对象都将被垃圾收集。
来自 SoftReference 的 javadoc:
所有对软可访问对象的软引用都保证在虚拟机抛出一个
OutOfMemoryError
我读到您的问题是询问“常规使用”的具体措辞,因此假设您已经了解强引用和弱引用。常规使用是指弱哈希映射包含的键也被其他一些数据结构(强)引用的情况。至少存在一个对密钥的强引用是“常规使用”。只要这个其他数据结构引用它,就不能对密钥进行垃圾收集。当另一个数据结构不再可达(指向它的指针不再存在)时,键也变得不可达。它不再经常使用,因为对它的唯一引用是映射中的弱引用。垃圾收集器最终可以回收它,并且映射消失了。
当您想通过子类化但不能扩展类型 C 时,就会出现这种情况:例如,当 C 是具有许多实现的接口时。您可以通过使用键类型为 C 的弱哈希映射以及要添加的新字段包装在新类 E 中来解决此问题。每当您创建一个 C 实例时,您还创建一个 E 实例并将该对添加到映射中,这然后用于在 C 实例的生命周期内访问新字段。当 C 实例变成垃圾时,映射和 E 实例也一样。这是自动的,因为 hashmap 很弱。如果不是,则必须以与在没有垃圾收集器的语言中显式释放存储相同的方式手动清理它。