我有一个系统不断耗尽磁盘空间,因为垃圾收集器不会释放保存文件句柄的对象足够快。 (文件一直在创建和删除,但由于进程仍然有一个打开的文件句柄,操作系统会将其保留在磁盘上。)
对象是共享的,因此简单的try { ... } finally { close(); }
不会。
在我看来,我最好的选择是在对象上实现引用计数,并在引用计数变为0时关闭文件句柄。但是,我不愿意自己实现它,因为我怀疑在那里关于并发性是微妙的问题。
可悲的是,谷歌搜索“java中的引用计数”并没有带来任何有用的结果。所以我的问题是:是否有任何资源(文章,示例代码,库)可以帮助实现引用计数?
答案 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
。它提供retain
和release
,它们使用线程安全且高效的AtomicFieldUpdater管理内部引用计数器。 release
将在同一线程上调用destroy
,并且由于对release
的不同调用之间的内存排序语义,您不必担心{{1 }},即使在多线程应用程序中也是如此。该类实现destroy
并提供一种方法AutoCloseable
,该方法仅调用close
。这使它可以在try-with-resources中使用。
没有万一最终确定机制尝试调用release
,以防您忘记调用release!最终确定提出了巨大的挑战,包括并发问题,甚至是过早的最终确定,尤其是在一般情况下。 (如果您坚持要使用终结器,则仍然可以使用终结器或性能更高的destroy
。)
相反,有一个聪明的泄漏检测系统。它使用类似的机制来完成。因为它的唯一职责是检测泄漏并记录调试信息,所以您无需执行任何操作即可使其正常工作(除非将其打开)。
vs Netty发生的主要变化是: