你有没有在任何项目中使用过PhantomReference?

时间:2009-10-21 06:36:29

标签: java reference

我对PhantomReference唯一了解的是,

  • 如果您使用 get() 方法,它将始终返回null而不是对象。它有什么用?
  • 使用PhantomReference,可确保无法从 finalize 方法重新生成对象。
  

但是这个概念/类的用途是什么?

     

你有没有在你的任何项目中使用它,或者你有任何我们应该使用它的例子吗?

10 个答案:

答案 0 :(得分:44)

我在simplistic, very specialized kind of memory profiler中使用了PhantomReference来监视对象的创建和销毁。我需要他们来追踪破坏。但这种方法已经过时了。 (它是在2004年针对J2SE 1.4编写的。)专业的分析工具功能更强大,更可靠,JMX或代理和JVMTI等新的Java 5功能也可用于此。

PhantomReference s(总是与Reference队列一起使用)优于finalize,它有一些问题,因此应该避免。主要是使对象再次可达。这可以通过终结者监护人习语来避免( - >在'有效Java'中阅读更多内容)。所以他们也是 new finalize

此外,PhantomReference s

  

允许您确定从内存中删除对象的确切时间。他们   实际上是确定这一点的唯一方法。通常不是这样   有用,但在某些非常具体的情况下可能派上用场   喜欢操纵大图像:如果你确定图像应该是   垃圾收集,你可以等到实际尝试之前   加载下一个图像,因此减少了可怕的OutOfMemoryError   有可能。 (引自enicholas。)

正如psd首先写的那样,Roedy Green有一个good summary of references

答案 1 :(得分:19)

来自Java词汇表的general diced-up table explanation

当然与PhantomReference documentation

重合
  

幻像引用对象,它们在收集器确定可以以其他方式回收它们之后排队。幻像引用最常用于以比Java终结机制更灵活的方式调度预先清理操作。

最后但并非最不重要的是,所有的血腥细节(这是一个很好的阅读):Java Reference Objects (or How I Learned to Stop Worrying and Love OutOfMemoryError)

快乐的编码。 (但回答这个问题,我只使用过WeakReferences。)

答案 2 :(得分:12)

幻影参考用法的

Great explanation

  

幻像引用是了解已删除对象的安全方法   从记忆里。例如,考虑一个处理的应用程序   大图像。假设我们想要将大图像加载到内存中   当大图像已经在内存中准备好垃圾时   集。在这种情况下,我们要等到旧图像   在加载新的之前收集。这里,幻影参考是   灵活安全的选择。旧图像的参考   一旦旧图像对象出现,它将在ReferenceQueue中排队   最终确定。收到该引用后,我们可以加载新图像   记忆中。

答案 3 :(得分:10)

我在commons-io项目中找到了PhantomReference的实用且有用的用例org.apache.commons.io.FileCleaningTrackerFileCleaningTracker将在标记对象被垃圾回收时删除物理文件 需要注意的是Tracker类,它扩展了PhantomReference类。

答案 4 :(得分:4)

这个应该在JAVA 9中被取消!
请改用java.util.Cleaner! (或旧{JRE上的sun.misc.Cleaner

原帖:


我发现PhantomReferences的使用与终结器方法几乎有相同的陷阱(但是一旦你做对了就会出现更少的问题)。 我为Java 8编写了一个小解决方案(一个非常小的框架来使用PhantomReferences)。 它允许在删除对象后使用lambda表达式作为回调。您可以注册应该关闭的内部资源的回调。 有了这个,我找到了一个适合我的解决方案,因为它使它更加实用。

https://github.com/claudemartin/java-cleanup

这里有一个小例子来说明如何注册回调:

  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);
    }

然后有更简单的自动关闭方法,与上面的方法大致相同:

this.registerAutoClose(this.resource);

回答你的问题:

  

[然后使用它]

你无法清理那些不存在的东西。但它可能有资源仍然存在,需要清理,以便删除它们。

  

但是这个概念/类的用途是什么?

除了调试/记录之外,它不一定要做任何其他任何效果。或者也许是统计数据。 我认为它更像是来自GC的通知服务。 您可能还希望使用它来删除一旦删除对象后变得无关紧要的聚合数据(但可能有更好的解决方案)。 示例通常会提到要关闭的数据库连接,但我不知道这是一个如此好的主意,因为您无法处理事务。应用程序框架将为此提供更好的解决方案。

  

你有没有在你的任何项目中使用过这个,或者你有任何我们应该使用它的例子吗?或者这个概念仅仅是为了采访的观点;)

我主要用它来记录。因此,我可以跟踪已删除的元素,并查看GC的工作原理并进行调整。我不会以这种方式运行任何关键代码。如果需要关闭某些东西,那么应该在try-with-resource-statement中完成。 我在单元测试中使用它,以确保我没有任何内存泄漏。和jontejj一样。但我的解决方案更为通用。

答案 5 :(得分:3)

我在单元测试中使用了PhantomReference来验证被测代码是否没有对某个对象进行不必要的引用。 (Original code

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>());
}
}

test

@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>");
    }
}

答案 6 :(得分:2)

通常使用WeakReference PhantomReference更合适。这避免了在垃圾收集器清除/排队WeakReference之后能够复活对象的某些问题。通常差异无关紧要,因为人们没有玩傻猫。

使用PhantomReference往往会更具侵入性,因为您无法假装get方法有效。例如,你不能写一个Phantom[Identity]HashMap

答案 7 :(得分:1)

  

如果你使用它的get()方法,它将始终返回null,而不是   宾语。 [然后是什么使用它]

调用(而不是get())的有用方法是isEnqueued()referenceQueue.remove()。您可以调用这些方法来执行某些操作,这些操作需要在对象的最后一轮垃圾收集中进行。

第一次是在对象调用了finalize()方法的时候,所以你也可以在那里放置关闭钩子。然而,正如其他人所说,可能有更多确定的方法来执行清理或在垃圾收集前后进行任何操作,或者更常见的是,在对象的生命周期结束时。

答案 8 :(得分:1)

我在Jetty的LeakDetector类中发现了PhantomReferences的另一种实际用法。

Jetty使用LeakDetector类来检测客户端代码是否获取了资源但从不释放资源,而LeakDetector类为此目的使用了PhantomReferences

答案 9 :(得分:0)

这里是一个 generic example of using it:在这种情况下,对于矢量编辑器中的一些 Swing 代码,当它们很容易回收时,很容易在绘制循环内创建数万个 AffineTransform 实例并重复使用,这表明它是分析中的一个重要瓶颈。在逐行处理日志文件时,我使用了相同的模式来重用 CharBuffer 实例。基本上模式是:你有一些数据结构创建起来很昂贵,而且你可以完全重置状态,而不是每次都创建一个新的。因此,您创建了一个 PhantomReference 子类,该子类强烈引用您要回收和重用的对象,其所指对象是可以引用该对象的事物;要跟踪回收对象的安全时间,您可以

  1. 返回对象的外观,该外观实现相同的接口或足够接近的东西(例如包装 CharBuffer 的 CharSequence 实现),并将其用作 PhantomReference 或
  2. 调用者向您传递对他们自己的引用,因此您可以在调用者超出范围时回收对象

换句话说,这里的模式是您要求队列告诉您每个可以了解某些缓存内容的对象何时消失,以便您可以将其重新用于另一个调用者。