【问题标题】:Using ReferenceQueue and WeakReference使用 ReferenceQueue 和 WeakReference
【发布时间】:2012-06-04 07:52:48
【问题描述】:

我想在 Closeable 对象不再被其他线程引用时正确关闭它。

我写了一些小测试,但是在对象入队后,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();
  }

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

【问题讨论】:

    标签: java


    【解决方案1】:

    首先,如果只是关于关闭,请使用PhantomReference。 接下来,从引用队列中,poll() 不保证您会取回引用。并且您将永远无法取回实际对象(所指对象)。

    如果您想确保您的Closeables 已关闭,您必须自己跟踪它们,让我们在Map&lt;Reference&lt;?&gt;, Closeable&gt; 中说。然后当你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()

    【讨论】:

    • 您好,感谢您的快速回复,这是关于 Lucene 的 IndexSearcher 的。
    • 我刚试过,在 System.gc(); 之后ReferenceQueue 大小为零。
    • @yan 无法保证您的对象在System.gc() 之后会获得 GCes,即使您退出 JVM 也是如此。您不为您的业务逻辑使用最终确定。它仅用于特殊目的,例如缓存和(最终)防止资源泄漏。你不能在终结器中说commit()rollback() DB 事务
    • 谢谢你,你是对的,这就是为什么我只使用 Closeable 接口来只允许关闭资源。而且我不介意在内存用完之前不会 GC。
    • IndexSearcher 是一个非常重量级的对象,我看不出这种方法怎么可能是理智的。 IndexSearcher 通常在整个应用程序期间保持为单例。现代 Lucene 版本应使用 SearcherManagerNRTManager 来获得开箱即用的完整维护周期。
    【解决方案2】:

    这里我想提几点

    1. get() 方法不适用于完成任务 get() 方法的目的是访问所指对象,因为我们使用 WeakReference 对象来引用没有直接引用的对象(即不可强可达)。 任何时候都可以对所指对象进行垃圾回收。 get() 方法预计会返回引用对象,直到它没有被垃圾回收,当它被垃圾回收时返回 null。 这是有道理的,因为引用对象在垃圾回收后不可用。 get() 方法不适用于关闭 Closable 对象等完成任务。

    2. 完成最终任务的方法并不简单。 完成完成任务的方法是在垃圾回收后将WeakReference 对象排入ReferenceQueue 队列后检索它。 并称之为 clear() 方法。您不能让引用对象来完成最终确定。因为在这一点上,人们相信所指对象 已被垃圾回收。方法是拥有一个将扩展 WeakReference 的 Reference 对象的自定义实现。您可以覆盖 clear() 放置任何清理逻辑的方法。您仍然不能在实现中对引用对象有任何引用,因为它会阻止引用对象 从被垃圾收集。如果你必须清理一些状态,例如引用对象有一些可关闭的对象或连接等,它们需要是 在实现下保存为实例变量,并在 clear 方法中它们可以最终确定。 当clear() 方法被调用时,终结逻辑将被执行。因此,Reference 对象将有助于最终确定。

    3. 上述方法在这里仍然没有用 因为即使WeakReference 对象的自定义实现也不能保持对引用对象本身的引用,因为它会阻止引用对象被垃圾收集。 您正面临这样的困难,因为将WeakReference 用于Closeble 并期望将其作为事后活动关闭不是正确的用例。 因为关闭 Closeble 没有资格成为事后活动。事后活动是在一个对象被垃圾收集后完成的,即它没有它的状态 一点也不。关闭Closeble 对象是一个预先确定的活动,即预期在 finally 块中完成,或者在创建它的线程中完成。 在Closeble 的垃圾收集之后延迟它是没有意义的。 ReferenceQueue 旨在用于死后活动,而不是用于死前活动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      • 2011-09-25
      • 1970-01-01
      相关资源
      最近更新 更多