问题标签 [phantom-reference]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
java - Soft-/Weak-/PhantomReferences 清除对已引用跟踪对象的对象的引用的基本原理
Soft
-、Weak
- 和s的文档PhantomReference
都包含与以下类似的行(取自PhantomReference
):
那时,它将自动清除对该对象的所有幻像引用以及对该对象可从中访问的任何其他幻像可访问对象的所有幻像引用。
让我困惑的部分是关于其他幻影可到达对象的部分。
如果我理解正确,这描述了这种情况:
对象:
- 一个
- 乙
参考:
->
: 强参考-P->
: 幻影参考
所以由于某种原因,垃圾收集器还没有确定这B
只是幻象可达的。现在,如果A
变为幻像可达并且垃圾收集器检测到这一点,则需要(根据上面引用的文档)也清除对B
.
文档有什么要求吗?如果其他供应商要开发 JVM,这似乎是一个相当大的负担。
java - 为什么自从 java 9 PhantomReference java doc 声明它专用于 POST-mortem 清理操作,尽管它之前是 PRE-mortem
PhantomReference
java 8和更少的 java doc 看起来像这样:
幻像引用对象,在收集器确定它们的引用对象可能会被回收后排队。幻影引用最常用于以比 Java 终结机制更灵活的方式调度事前清理操作。如果垃圾收集器在某个时间点确定幻影引用的所指对象是幻影可到达的,那么在那个时间或稍后的某个时间它将将该引用入队。
为了确保可回收对象保持不变,可能无法检索幻像引用的所指对象:幻像引用的 get 方法始终返回 null。
与软引用和弱引用不同,幻像引用在排队时不会被垃圾收集器自动清除。通过幻像引用可访问的对象将保持不变,直到所有此类引用都被清除或自身变得不可访问
PhantomReference
java 9及更高版本的 java doc 如下所示:
幻像引用对象,在收集器确定它们的引用对象可能会被回收后排队。幻影引用最常用于安排事后清理操作。假设垃圾收集器在某个时间点确定对象是幻影可达的。那时,它将自动清除对该对象的所有幻像引用以及对该对象可从中访问的任何其他幻像可访问对象的所有幻像引用。在同一时间或稍后的某个时间,它会将那些在引用队列中注册的新清除的幻像引用排入队列。
为了确保可回收对象保持不变,可能无法检索幻像引用的所指对象:幻像引用的 get 方法始终返回 null。
java 9 中的PhantomReference行为是否发生了变化?还是只是 java 创始人重新考虑了该课程的奉献精神?
java - 为什么 PhantomReference 的入队比 WeakReference 或 SoftReference 需要更多的 GC 周期?
我决定在一个单独的主题中继续https://stackoverflow.com/a/41998907/2674303 。
让我们考虑以下示例:
Java 11 打印如下:
前 2 行可以更改顺序。看起来它们是并行工作的。
最后一行有时会打印 2 个 gc 民意调查,有时会打印 3 个
所以我看到 PhantomReference 的入队需要更多的 GC 周期。怎么解释?它是否在文档中的某处提到(我找不到)?
附言
弱参考 java 文档:
假设垃圾收集器在某个时间点确定一个对象是弱可达的。那时,它将原子地清除对该对象的所有弱引用以及对通过强引用和软引用链可以访问该对象的任何其他弱可达对象的所有弱引用。同时它将声明所有以前的弱可达对象都是可终结的。在同一时间或稍后的某个时间,它会将那些注册到引用队列中的新清除的弱引用排入队列
PhantomReference java 文档:
假设垃圾收集器在某个时间点确定对象是幻影可达的。那时,它将自动清除对该对象的所有幻像引用以及对该对象可从中访问的任何其他幻像可访问对象的所有幻像引用。在同一时间或稍后的某个时间,它会将那些在引用队列中注册的新清除的幻像引用排入队列
区别对我来说不清楚
PS(我们正在谈论具有非平凡终结方法的对象)
我从@Holger 那里得到了我的问题的答案:
他(没有性别歧视,但我想是的)向我指出了java 文档,并注意到 PhantomReference 与软引用和弱引用相比包含额外的短语:
一个对象是弱可达的,如果它既不是强可达也不是软可达,但可以通过遍历弱引用来达到。当对弱可达对象的弱引用被清除时,该对象就有资格进行终结。
一个对象是幻影可达的,如果它既不是强可达的,也不是软可达的,也不是弱可达的,它已经完成了,并且一些幻象引用引用了它
我的下一个问题是关于它已经完成意味着什么我预计这意味着 finalize 方法已经完成
为了证明这一点,我修改了这样的应用程序:
我看到以下输出:
应用程序挂起。我认为这是因为对于弱/软引用,GC 以以下方式工作:一旦 GC 检测到该对象是弱/软可到达的,它就会并行执行 2 个操作:
- 将 Weak/Soft 排入已注册的 ReferenceQueue 实例
- 运行 finalize 方法
因此,对于添加到 ReferenceQueue 中,对象是否复活并不重要。
但是对于 PhantomReference 操作是不同的。一旦 GC 检测到该对象是 Phantom Reachable,它就会按顺序执行以下操作:
- 运行 finalize 方法
- 检查该对象仍然只有 phantomReachable(检查该对象在 finalize 方法执行期间没有复活)。并且只有当对象是 GC 时才会将幻像引用添加到 ReferenceQueue
但是@Holger说它已经完成意味着JVM启动了finalize()方法调用,并且为了将PhantomReference添加到ReferenceQueue中,它是否完成并不重要。但看起来我的例子表明它真的很重要。
坦率地说,我不明白根据添加到参考队列中以获取弱参考和软参考的区别。想法是什么?
java - 为什么我们必须手动清除 PhantomReference?
从javadoc:
与软引用和弱引用不同,幻像引用在排队时不会被垃圾收集器自动清除。通过幻像引用可访问的对象将保持不变,直到所有此类引用都被清除或自身变得不可访问。
一旦放入 ReferenceQueue,幻象链接的引用对象就被认为是“死的”,程序员的代码无法以任何方式访问它。那么为什么 JVM 等待手册clear()
呢?对于弱裁判,它不会。
java - 为什么确切地应该首选 PhantomReference 来完成?
它们都可以用于清理,几乎无法保证,但 PR 需要更多的线束编码。那么,有两种选择,为什么我必须偏爱另一种呢?
Javadoc 9将 finalize 描述为非常有问题,但这并不能使其替代方案自动变得更好,对吧?
javadoc 还描述PhantomReference
为“在对象变得无法访问时提供更灵活和更有效的方法来释放资源”,但没有指定原因。好吧,我想这些家伙知道一些秘密,但我想知道 - 这个选择不能更明显吗?
区别
以下是我发现的 finalize (FZ) 和 pantom reference (PR) 之间的所有差异,如果我遗漏了什么,请纠正我。
可用于清理动作?
- 两者都是。
需要一个新线程来维护?
- PR:是的,您必须定义一个队列观察线程才能尽快进行清理
- FZ:没有
需要一个新的类来定义?
- PR:是的,您必须扩展
PhantomReference
才能有意义地采取行动 - FZ:没有
- PR:是的,您必须扩展
清理处理器可以访问引用对象吗?
- 公关:没有
- FZ:是的,这很方便
它在我的个人实践中可靠吗?
- 两者都是。
会导致性能问题、死锁和挂起吗?
- 两者都是。取决于你的代码,不是吗?
清理处理器中的错误会导致资源泄漏吗?
- 两者都是。取决于你的代码,不是吗?
如果不再需要可以取消吗?
- 公关:是的
- FZ:不,如果严格来说,直接
return
那么糟糕吗?
是否指定了多个实例之间的调用顺序?
- 公关:没有信息
- FZ:否 - “在调用以完成不同对象的方法时未指定排序”(java.lang.Object)
保证调用?
- PR:没有信息 - 您只能“请求通知对象可达性的变化”(java.lang.ref)
- FZ:否 - “如果有的话,只有在无限期延迟之后才能在可终结对象上调用 finalize 方法”(java.lang.Object)
关于时间的任何保证?
- PR:否-“在垃圾收集器确定引用对象的可达性已更改为与引用类型对应的值之后的一段时间”(java.lang.ref)
- FZ:否 - “Java 编程语言没有指定多久会调用终结器”(JLS),“如果有的话,只有在无限延迟之后才可能在可终结对象上调用终结方法”(java.lang.目的)
加工过程中可以
this
复活吗?- PR:不,这还不错
- FZ:是的,官方支持
链接:
java - 如何使用对正在注册的对象的引用来运行清理操作
从 java 9 开始,我们终于有了Cleaner
. 但是在文档中特别声明:
每个清洁器独立运行,管理待处理的清洁操作并在清洁器不再使用时处理线程和终止。
注册一个对象引用和相应的清理操作会返回一个 Cleanable。最有效的用法是在对象关闭或不再需要时显式调用 clean 方法。
清理操作是一个 Runnable,当对象变为幻像可达时最多调用一次,除非它已经被显式清理。
请注意,清理操作不得引用正在注册的对象。如果是这样,该对象将不会变为幻影可达,并且不会自动调用清理操作。
不幸的是,这正是我所需要的:我要注册的操作需要引用对象本身(通常它只是调用一个方法)。
由于 Cleaner 看起来不可能做到这一点,有没有办法使用 Phantom 或弱/软引用来做到这一点?
我看了看它们,它们看起来相当复杂,所以在投入大量时间认真研究它们之前,我想提前知道这样的事情是否可能
Ps:显然,幻影引用现在似乎在事后执行
java - Java 终结器的替代方案
我正在使用 Mysql GET_LOCK在分布式系统中实现锁定服务。在调用我的 getLock() 方法时,如果客户端获得了锁,我会在数据库中创建一个条目,并在释放锁时删除该条目。
假设调用客户端将在达到其目的后释放锁。但是,我想确保在客户端不释放它或不进行适当清理的情况下释放锁。
一种方法是在我的锁对象上使用 finalize 方法在调用 finalize 时释放它。然而它并不理想,增加了复杂性并且在 Java 9 中被弃用。我读到了 Phantom 引用,它比终结器更好,但它的复杂性也很高。我把它作为我最后的手段。
有没有更简单、更少依赖 JVM 的方法来处理这个用例?
java - 参考队列始终为空
当无法再访问本机资源时,我正在尝试清理它。该资源提供了一种清理分配资源(内存、线程等)的方法。为此,我使用了 Phantom Reference。
当库用户提供新配置时,应异步创建该资源。
问题是,ReferenceQueue 总是空的。我不引用文件之外的本机资源。即使在这种情况下,poll() 方法也会返回 null。所以我无法清理资源并导致内存泄漏。我怎样才能避免这种情况?
您可以在下面找到示例代码。我使用了 JDK 8。