你有没有在任何项目中使用PhantomReference?
我对PhantomReference
唯一的了解是,
- 如果你使用它的
get()
方法,它将总是返回null
而不是对象。 它有什么用处? - 通过使用
PhantomReference
,可以确保该对象不能从finalize
方法中复制。
但是这个概念/课程的用途是什么?
你有没有在你的任何项目中使用过这个,或者你有什么样的例子来使用它?
我在一个简单的,非常专业的内存分析器中使用PhantomReference
来监视对象的创build和销毁。 我需要他们跟踪破坏。 但是这种方法已经过时了。 (它是在2004年针对J2SE 1.4编写的。)专业的分析工具function更加强大和可靠,像JMX或者代理和JVMTI等更新的Java 5特性也可以用于此。
PhantomReference
(总是与Reference队列一起使用)优于finalize
,因此存在一些问题,因此应该避免。 主要使对象再次可达。 这可以通过终结者监护人惯用法( – >在“Effective Java”中阅读更多内容)来避免。 所以他们也是新的敲定 。
此外, PhantomReference
s
允许您确定何时从内存中删除对象。 他们实际上是唯一的方法来确定这一点。 这通常不是很有用,但是在某些非常特殊的情况下可能会派上用场,比如处理大图像:如果确实知道应该对图像进行垃圾回收,那么可以等到它实际上在尝试加载下一个图像之前,因此使可怕的OutOfMemoryError不太可能。 (引自enicholas 。)
正如psd首先写的,Roedy Green 对参考文献有很好的总结 。
来自Java术语表的一般性切割表格说明 。
这当然与PhantomReference文档一致:
幻象参考对象,在收集器之后入队确定其对象可以被回收。 Phantom引用最常用于以比Java finalization机制更为灵活的方式调度验前清理操作。
最后但并非最不重要的,所有的血淋淋的细节( 这是一个很好的阅读 ): Java参考对象(或者我怎么学会停止担心和爱OutOfMemoryError) 。
快乐的编码。 (但是要回答这个问题,我只使用了WeakReferences。)
我在commons-io项目中find了一个实用且有用的PhantomReference
用例,它是org.apache.commons.io.FileCleaningTracker
。 FileCleaningTracker
将在其标记对象被垃圾收集时删除物理文件。
需要注意的是扩展PhantomReference
类的Tracker
类。
幻影参考用法的很好的解释 :
幻影参考是安全的方式来知道一个对象已经从内存中删除。 例如,考虑处理大图像的应用程序。 假设我们要在大图像已经在准备好垃圾收集的内存中的时候把一个大的图像加载到内存中。 在这种情况下,我们希望等到旧图像被收集后再加载一个新的图像。 在这里,幻影参考是灵活和安全的selectselect。 一旦旧图像对象完成,旧图像的引用将被排入到ReferenceQueue中。 收到该参考后,我们可以将新的图像加载到内存中。
我在unit testing中使用了一个PhantomReference来validation被测代码没有对某些对象进行不安全的引用。 ( 原始码 )
import static com.google.common.base.Preconditions.checkNotNull; import static org.fest.assertions.Assertions.assertThat; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import com.google.common.testing.GcFinalization; /** * Helps to test for memory leaks */ public final class MemoryTester { private MemoryTester() { } /** * A simple {@link PhantomReference} that can be used to assert that all references to it is * gone. */ public static final class FinalizationAwareObject extends PhantomReference<Object> { private final WeakReference<Object> weakReference; private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue) { super(checkNotNull(referent), referenceQueue); weakReference = new WeakReference<Object>(referent, referenceQueue); } /** * Runs a full {@link System#gc() GC} and asserts that the reference has been released * afterwards */ public void assertThatNoMoreReferencesToReferentIsKept() { String leakedObjectDescription = String.valueOf(weakReference.get()); GcFinalization.awaitFullGc(); assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue(); } } /** * Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff} * has been garbage collected. Call * {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect * all references to {@code referenceToKeepTrackOff} be gone. */ public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff) { return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>()); } }
和testing :
@Test public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception { Object holdMeTight = new String("Hold-me-tight"); FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight); try { finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept(); fail("holdMeTight was held but memory leak tester did not discover it"); } catch(AssertionError expected) { assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>"); } }
这应该是JAVA 9废弃!
改用java.util.Cleaner
! (或者更老的JRE上的sun.misc.Cleaner
)
原文:
我发现PhantomReferences的使用与finalizer方法有几乎相同的缺陷(但是一旦你正确的使用它,问题会更less)。 我已经为Java 8编写了一个小型解决scheme(一个使用PhantomReferences的非常小的框架)。它允许使用lambdaexpression式作为在对象被删除后运行的callback。 您可以注册应该closures的内部资源的callback。 有了这个,我find了一个适合我的解决scheme,因为它使它更实用。
https://github.com/claudemartin/java-cleanup
下面是一个小例子,展示一个callback是如何被注册的:
class Foo implements Cleanup { //... public Foo() { //... this.registerCleanup((value) -> { try { // 'value' is 'this.resource' value.close(); } catch (Exception e) { logger.warning("closing resource failed", e); } }, this.resource); }
然后有更简单的自动closures方法,就像上面这样做:
this.registerAutoClose(this.resource);
回答你的问题:
[那么它的用途是什么]
你不能清理不存在的东西。 但是它可能有一些仍然存在的资源需要清理,以便将它们移除。
但是这个概念/课程的用途是什么?
除了debugging/日志logging之外,没有任何其他的作用。 或者也许是为了统计。 我觉得它更像是来自GC的通知服务。 您也可以使用它来删除聚集的数据,一旦对象被删除变得不相关(但可能有更好的解决scheme)。 例子经常提到数据库连接被closures,但我不明白这是一个好主意,因为你不能处理事务。 应用程序框架将为此提供更好的解决scheme。
你有没有在你的任何项目中使用过这个,或者你有什么样的例子可以使用它? 或者这个概念只是为了面试的angular度而做的)
我主要用它来logging。 所以我可以跟踪删除的元素,看看GC是如何工作,可以调整。 我不会以这种方式运行任何关键代码。 如果有什么东西需要closures的话,那么应该用一个试用资源语句来完成。 我在unit testing中使用它,以确保我没有任何内存泄漏。 和jontejj一样。 但是我的解决scheme有点更一般。
在PhantomReference
更合适的地方使用WeakReference
是很常见的。 这可以避免在WeakReference
被垃圾收集器清除/入队之后能够重新生成对象的问题。 通常情况下,差异并不重要,因为人们并不是在玩愚蠢的玩家。
使用PhantomReference
往往会有点侵入性,因为你不能假装get
方法工作。 例如,你不能写一个Phantom[Identity]HashMap
。
如果使用get()方法,它将总是返回null,而不是对象。 [那么它的用途是什么]
调用(而不是get()
)的有用方法是isEnqueued()
或referenceQueue.remove()
。 您可以调用这些方法来执行一些需要在对象的垃圾收集的最后一轮进行的操作。
第一次是对象的finalize()
方法被调用的时候,所以你可以把closures钩子也放在那里。 然而,正如其他人所说的那样,可能有更确定的清理方式,或者在垃圾收集之前和之后发生的任何行动,或者更普遍的是在物体的报废之后。