使用ReferenceQueue和WeakReference

12

我希望能够在其他线程不再引用可关闭对象时正确关闭它。

我编写了一些小测试,但在对象被入队后,get方法返回null,也就是poll方法返回的正确对象没有引用。

  public static void main(String[] args)
  {
   ReferenceQueue<Closeable> reaped = new ReferenceQueue<Closeable>();
   Closeable s = <SOME CLOSEABLE IMPL>;
   WeakReference<Closeable> ws = new WeakReference<Closeable>(s, reaped);
   s = null;

   System.gc();
   Closeable ro = (Closeable)reaped.poll().get();
   ro.close();
  }

提前致谢。 任何帮助都将不胜感激。

2个回答

8

首先,如果只是关闭对象,可以使用PhantomReference。 接下来,从引用队列中,poll()不能保证您会获得相应的引用,而且您永远不会获得实际对象(被引用的对象)。

如果您想确保您的Closeable关闭,您必须自己跟踪它们,比如在一个Map<Reference<?>, Closeable>中。然后当您poll()引用队列时,最终会得到ref,然后您必须使用它从映射中获取Closeable

   class MyThing {
      Closeable c;
   }

   Map<Reference<MyThing>, Closeable> m = new HashMap();
   ReferenceQueue<MyThing> reaped = new ReferenceQueue<MyThing>();

   MyThing mt = new MyThing();
   mt.c = new MyClosable();

   Reference<MyThing> pref = new PhantomReference<MyThing>(mt, reaped);
   m.put(pref, mt.c);

   mt = null;


   System.gc();
   Reference<MyThing> rf = reaped.poll();
   while (rf != null) {
     m.get(rf).close(); 
     rf = reaped.poll();
   }

注意:如果你没有真正的理由去这么做,或者你不了解自己正在做什么,请不要这样做。

你可以在 finally 中关闭你的文件。顺便说一下,如果涉及到文件、套接字等等,它们会被自动关闭(它们已经实现了 finalize())。


1
你好,感谢你的迅速回复。这是关于Lucene中IndexSearcher的问题。 - yan
我刚试了一下,在 System.gc(); 之后,ReferenceQueue 的大小为零。 - yan
@yan,即使在退出JVM时,也不能保证您的对象会在System.gc()之后得到垃圾回收。您不应该将终结器用于业务逻辑,它仅用于特殊目的,如缓存和(最终)防止资源泄漏。您不能在终结器中执行诸如commit()rollback()数据库事务的操作。 - Op De Cirkel
谢谢,你是对的,这就是为什么我只使用Closeable接口来允许关闭资源。我不介意对象在内存耗尽之前不会被垃圾回收。 - yan
1
IndexSearcher 是一个非常重量级的对象,我无法想象这种方法怎么可能是明智的。通常情况下,IndexSearcher 会作为整个应用程序的单例一直存在。现代 Lucene 版本应该使用 SearcherManagerNRTManager 来获得完整的维护周期。 - Marko Topolnik

0

这里我想提几点

  1. get()方法不应该用于完成最终任务 get()方法的目的是访问引用对象,因为我们使用WeakReference对象来引用一个没有直接引用的对象(即不是强可达的)。 引用对象可以随时被垃圾回收。期望get()方法在引用对象未被垃圾回收之前返回引用对象,并在引用对象被垃圾回收后返回null。 这是有道理的,因为垃圾回收后引用对象不再可用。 get()方法不应用于执行关闭Closable对象等最终化任务。

  2. 进行最终化任务的方法并不直接。 进行最终化任务的方法是在垃圾回收后,从ReferenceQueue中检索WeakReference对象。 并调用它的clear()方法。您无法获取引用对象以执行最终化。因为此时认为引用对象已被垃圾回收。 实现自定义引用对象的方法是扩展WeakReference。可以覆盖clear()方法以放置任何清理逻辑。 在实现中仍然无法引用引用对象,否则将阻止引用对象被垃圾回收。 如果需要清理某些状态,例如引用对象具有某些可关闭对象或连接等,则需要将它们保存为实例变量,然后在清除方法中进行最终化。 当调用clear()方法时,将执行最终化逻辑。因此,引用对象将促进最终化。

  3. 但以上方法在这里无用 即使是自定义实现的WeakReference对象,也不能保留对引用对象本身的引用,否则将阻止引用对象被垃圾回收。 您面临这样的困难,因为使用WeakReference关闭Closeble并期望作为死后活动进行关闭不是正确的用例。 因为关闭Closeble不合格作为死后活动。死后活动是在对象被垃圾回收后完成的,即它根本没有其状态。 关闭Closeble对象是一种预先活动,即预计在finally块或在创建它的线程中完成。 没有必要在Closeble的垃圾回收之后延迟它。 ReferenceQueue的设计用于死后活动,而不是预先活动。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接