用Java引用计数

时间:2010-10-17 11:05:26

标签: java concurrency design-patterns

我有一个系统不断耗尽磁盘空间,因为垃圾收集器不会释放保存文件句柄的对象足够快。 (文件一直在创建和删除,但由于进程仍然有一个打开的文件句柄,操作系统会将其保留在磁盘上。)

对象是共享的,因此简单的try { ... } finally { close(); }不会。

在我看来,我最好的选择是在对象上实现引用计数,并在引用计数变为0时关闭文件句柄。但是,我不愿意自己实现它,因为我怀疑在那里关于并发性是微妙的问题。

可悲的是,谷歌搜索“java中的引用计数”并没有带来任何有用的结果。所以我的问题是:是否有任何资源(文章,示例代码,库)可以帮助实现引用计数?

3 个答案:

答案 0 :(得分:8)

不要依赖垃圾收集器。故意设计不可靠。

如果“共享”意味着您在代码中使用了几个位置,那么您不能只关闭它,我建议您更改代码以拥有一个中央文件池,您可以在其中“签出”文件句柄在本地代码中使用。然后close()过程将文件句柄返回到池。跟踪您的句柄,当给定文件的所有句柄都返回到池中时,您将关闭该文件。

答案 1 :(得分:3)

您可以将对象包裹在WeakReference内,然后使用ReferenceQueue进行引用计数。但是,当您的句柄不再被引用时,您似乎只想发现,因此您根本不需要计算。

但是同样的方法(即WeakReference)会做;当指示对象变为null时关闭句柄。但是,您可能需要一个定制的WeakReference子类以及对文件句柄的额外引用,以便您可以关闭它(否则您将无法访问文件句柄)。例如:

public class WeakFileReference extends WeakReference<File> {

  private final File  handle;
  public WeakFileReference(File handle, ReferenceQueue q) {
    super(handle, q);
    this.handle = (File)handle.clone();
  }
}

我没有完全检查过,无法确定您在程序中使用File对象的方式:我假设您正在共享File个实例。

答案 2 :(得分:1)

  

遗憾的是,谷歌搜索“ java中的引用计数”不会带来任何有用的结果。

可悲的是,这在8年后仍然如此。

但是,不再!我已经抽出Netty's个参考计数位,将它们精打了一部分,并制成了一个单独的库almson-refcount

基本引用计数功能简单明了。只有一个基类ReferenceCountedObject。它有一个可覆盖的方法destroy。它提供retainrelease,它们使用线程安全且高效的AtomicFieldUpdater管理内部引用计数器。 release将在同一线程上调用destroy,并且由于对release的不同调用之间的内存排序语义,您不必担心{{1 }},即使在多线程应用程序中也是如此。该类实现destroy并提供一种方法AutoCloseable,该方法仅调用close。这使它可以在try-with-resources中使用。

没有万一最终确定机制尝试调用release,以防您忘记调用release!最终确定提出了巨大的挑战,包括并发问题,甚至是过早的最终确定,尤其是在一般情况下。 (如果您坚持要使用终结器,则仍然可以使用终结器或性能更高的destroy。)

相反,有一个聪明的泄漏检测系统。它使用类似的机制来完成。因为它的唯一职责是检测泄漏并记录调试信息,所以您无需执行任何操作即可使其正常工作(除非将其打开)。

vs Netty发生的主要变化是:

  • 所有引用计数对象的实际可用基类。
  • 界面更优美,方法更少。
  • 更简单,更简洁的代码,减少了麻烦。
  • 对检漏仪的文档,预设和输出进行了改进。