我们有一个用Java编写的相当大而复杂的应用程序,它运行在Gridgain包之上。我遇到的问题是这个应用程序将在每个请求开始前大约一天处理请求,导致java.nio.channels.ClosedByInterruptException类型的异常。
我的假设是应用程序没有释放文件句柄,并且在连续使用一天之后它用完了并且不能再继续处理请求(每个请求都需要从每个网格节点读取多个文件)。我们已将大部分文件IO操作包含在诸如此类的类中
package com.vlc.edge;
import com.vlc.common.VlcRuntimeException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public final class BufferedReaderImpl implements BufferedReader {
private java.io.BufferedReader reader;
public BufferedReaderImpl(final String source) {
this(new File(source));
}
public BufferedReaderImpl(final File source) {
try {
reader = new java.io.BufferedReader(new FileReader(source));
} catch (FileNotFoundException e) {
throw new VlcRuntimeException(e);
}
}
public BufferedReaderImpl(final Reader reader) {
this.reader = new java.io.BufferedReader(reader);
}
public String readLine() {
try {
return reader.readLine();
} catch (IOException e) {
throw new VlcRuntimeException(e);
}
}
public void close() {
try {
reader.close();
} catch (IOException e) {
throw new VlcRuntimeException(e);
}
}
}
我认为问题是这个设计没有明确释放文件句柄,我建议的解决方案就是添加一个像这样的finalize方法
protected void finalize() throws Throwable
{
reader.close();
super.finalize();
}
将明确地执行此操作。问题(最后)是否可能产生任何影响。像java.io.BufferedReader这样的类已经有一些处理这类问题的机制吗?
编辑:也非常感谢这里检查这是否真的是问题...即有没有办法查询正在运行的JVM并询问它的文件句柄分配?答案 0 :(得分:12)
不能依赖终结者进行调用。这对资源管理来说不是一个好方法。 Java中的标准结构是:
InputStream in = null;
try {
in = ...;
// do stuff
} catch (IOException e) {
// error
} finally {
if (in != null) { try { in.close(); } catch (Exception e) { } }
in = null;
}
您可能希望将这些句柄包装在一个类中,但这不是一个强大的方法。
答案 1 :(得分:5)
覆盖finalize()
几乎没有意义。如果句柄被垃圾收集并最终确定,那么java.io.BufferedReader
的实例也将被关闭。
可能(根据规范)句柄是垃圾收集但未最终确定,但这不太可能。
您可以尝试使用PhantomReference
来清理未使用的文件句柄,但我的猜测是您的BufferedReaderImpl
实例仍然从某处引用(例如Map
中的值来自文件名打开句柄)这就是阻止它们被关闭的原因(在这种情况下,终结者将无济于事。)
答案 2 :(得分:1)
Java规范说,不能保证“finalize()
”将被执行。您的代码必须自己明确关闭FileReader
。