Soft-/Weak-/PhantomReferences 清除对引用被跟踪对象的对象的引用的理由
Rationale for Soft-/Weak-/PhantomReferences clearing references to objects which have reference to tracked object
Soft
-, Weak
- and PhantomReference
的文档都包含类似于以下内容的一行(摘自 PhantomReference
):
At that time it will atomically clear all phantom references to that object and all phantom references to any other phantom-reachable objects from which that object is reachable.
让我感到困惑的部分是关于其他幻影可及对象的部分。
如果我理解正确的话,这描述了这个案例:
对象:
- 一个
- B
参考文献:
->
: 强引用
-P->
: 幻影参考
-> A
-P-> B -> A
所以出于某种原因,垃圾收集器尚未确定 B
仅是幻象可达的。现在,如果 A
变为幻影可达并且垃圾收集器检测到这一点,则需要(根据上面引用的文档)清除对 B
.
的引用
文档要求这样做有什么理由吗?看来如果其他厂商要开发一个JVM,这将是一个相当大的负担。
我们首先要注意,这句话是从软引用和弱引用文档复制到 Java 9 的幻影引用文档,以适应该版本中所做的更改,但不是非常适合虚引用,所以它背后的基本原理可以更好地解释软引用和弱引用。
假设您有以下情况:
(weak)→ A
(weak)→ B (strong)→ A
从技术上讲,A
和 B
都是弱可达的,但我们可以改变这一点,在任一弱引用上调用 get()
方法,以检索对其所指对象的强引用.
当我们在第一个弱引用上执行此操作以检索对 A
的强引用时,对象 B
将保持弱可达性,但当我们执行此操作以获取对 A
的强引用时B
,由于从 B
到 A
的强引用,对象 A
也将变得强可达。
因此,我们有一个规则,如果对A
的弱引用被清除,对B
的弱引用必须被清除,否则,将有可能检索到一个强引用尽管对 A
的弱引用已被清除,但通过 B
对 A
的引用已被清除。为了安全起见,它必须以原子方式发生,因此不可能存在允许在两个引用的间隙之间检索对 B
的引用的竞争条件。
如前所述,这与幻影引用的相关性较小,因为它们不允许检索引用,但没有理由区别对待它们。
这里的重点是,考虑到垃圾收集器的实际工作方式,这并不是实际的负担。他们必须遍历所有活动引用,即强可达对象,并且所有未遇到的对象都是每次消除的垃圾。所以在遍历中遇到弱引用时,不会遍历引用对象,而是记住引用对象。完成遍历后,它将 运行 遍历所有遇到的引用对象,并查看引用对象是否已被标记为可通过不同路径访问。如果不是,则清除引用对象并链接入队。
举个例子:
(strong)→ A
(weak)→ B (strong)→ A
在这里,无论对 A
的强引用如何,B
都是弱可达的。当您消除对 A
的强引用时,B
仍然是弱可达的并且可能会排队。形式上,A
现在是弱可达的,但是 JVM 永远不会检测到 B
也是弱可达的。检测 A
是弱可达的唯一方法是遍历从弱可达 B
开始的参考图。但是没有实现这样做。垃圾收集器将简单地清除对 B
的弱引用,仅此而已。
Soft
-, Weak
- and PhantomReference
的文档都包含类似于以下内容的一行(摘自 PhantomReference
):
At that time it will atomically clear all phantom references to that object and all phantom references to any other phantom-reachable objects from which that object is reachable.
让我感到困惑的部分是关于其他幻影可及对象的部分。
如果我理解正确的话,这描述了这个案例:
对象:
- 一个
- B
参考文献:
->
: 强引用-P->
: 幻影参考
-> A
-P-> B -> A
所以出于某种原因,垃圾收集器尚未确定 B
仅是幻象可达的。现在,如果 A
变为幻影可达并且垃圾收集器检测到这一点,则需要(根据上面引用的文档)清除对 B
.
文档要求这样做有什么理由吗?看来如果其他厂商要开发一个JVM,这将是一个相当大的负担。
我们首先要注意,这句话是从软引用和弱引用文档复制到 Java 9 的幻影引用文档,以适应该版本中所做的更改,但不是非常适合虚引用,所以它背后的基本原理可以更好地解释软引用和弱引用。
假设您有以下情况:
(weak)→ A
(weak)→ B (strong)→ A
从技术上讲,A
和 B
都是弱可达的,但我们可以改变这一点,在任一弱引用上调用 get()
方法,以检索对其所指对象的强引用.
当我们在第一个弱引用上执行此操作以检索对 A
的强引用时,对象 B
将保持弱可达性,但当我们执行此操作以获取对 A
的强引用时B
,由于从 B
到 A
的强引用,对象 A
也将变得强可达。
因此,我们有一个规则,如果对A
的弱引用被清除,对B
的弱引用必须被清除,否则,将有可能检索到一个强引用尽管对 A
的弱引用已被清除,但通过 B
对 A
的引用已被清除。为了安全起见,它必须以原子方式发生,因此不可能存在允许在两个引用的间隙之间检索对 B
的引用的竞争条件。
如前所述,这与幻影引用的相关性较小,因为它们不允许检索引用,但没有理由区别对待它们。
这里的重点是,考虑到垃圾收集器的实际工作方式,这并不是实际的负担。他们必须遍历所有活动引用,即强可达对象,并且所有未遇到的对象都是每次消除的垃圾。所以在遍历中遇到弱引用时,不会遍历引用对象,而是记住引用对象。完成遍历后,它将 运行 遍历所有遇到的引用对象,并查看引用对象是否已被标记为可通过不同路径访问。如果不是,则清除引用对象并链接入队。
举个例子:
(strong)→ A
(weak)→ B (strong)→ A
在这里,无论对 A
的强引用如何,B
都是弱可达的。当您消除对 A
的强引用时,B
仍然是弱可达的并且可能会排队。形式上,A
现在是弱可达的,但是 JVM 永远不会检测到 B
也是弱可达的。检测 A
是弱可达的唯一方法是遍历从弱可达 B
开始的参考图。但是没有实现这样做。垃圾收集器将简单地清除对 B
的弱引用,仅此而已。