所以我有一个在 gridgain 之上运行的应用程序,并且在它开始表现得很滑稽之前进行了大约 12-24 小时的压力测试,并且非常成功。这段时间过后,应用程序将突然开始回复所有异常 java.nio.channels.ClosedByInterruptException 的查询(完整堆栈跟踪位于 http://pastie.org/664717
失败的方法是(编辑为使用@stephenc 反馈)
public static com.vlc.edge.FileChannel createChannel(final File file) {
FileChannel channel = null;
try {
channel = new FileInputStream(file).getChannel();
channel.position(0);
final com.vlc.edge.FileChannel fileChannel = new FileChannelImpl(channel);
channel = null;
return fileChannel;
} catch (FileNotFoundException e) {
throw new VlcRuntimeException("Failed to open file: " + file, e);
} catch (IOException e) {
throw new VlcRuntimeException(e);
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e){
// noop
LOGGER.error("There was a problem closing the file: " + file);
}
}
}
}
并且调用函数正确地关闭了对象
private void fillContactBuffer(final File signFile) {
contactBuffer = ByteBuffer.allocate((int) signFile.length());
final FileChannel channel = FileUtils.createChannel(signFile);
try {
channel.read(contactBuffer);
} finally {
channel.close();
}
contactBuffer.rewind();
}
该应用程序基本上用作分布式文件解析器,因此它执行大量此类操作(通常每个节点的每个查询会打开大约 10 个此类 channel )。似乎在一段时间后它无法打开文件,我无法解释为什么会发生这种情况,如果有人能告诉我可能导致这种情况的原因以及我如何进行跟踪,我将不胜感激它下来并修复它。如果它可能与文件句柄耗尽有关,我很想听听任何确定确定的提示......即在运行时查询 JVM 或使用 linux 命令行工具来查找有关当前打开的句柄的更多信息.
更新:我一直在使用命令行工具来查询 lsof 的输出,但未能看到文件句柄保持打开状态的任何证据......网格具有非常稳定的打开文件配置文件,我可以看到随着上述代码的执行而发生变化......但它总是返回到稳定数量的打开文件。
与此问题相关:Freeing java file handles
最佳答案
有几种情况可能无法关闭文件句柄:
- 可能还有一些其他代码可以打开文件。
- 可能还有其他一些代码调用了
createChannel(...)
而没有调用fillContactBuffer(...)
如果
channel.position(0)
抛出异常, channel 不会关闭。解决方法是重新排列代码,使以下语句位于try
block 内。channel.position(0); return new FileChannelImpl(channel);
编辑:查看堆栈跟踪,这两种方法似乎在不同的代码库中。我会将责任归咎于 createChannel
方法。它可能会泄漏,即使它不是问题的根源。它需要一个内部 finally
子句来确保在发生异常时关闭 channel 。
像这样的东西应该可以解决问题。请注意,您需要确保 finally
block 不会在成功时关闭 channel !
public static com.vlc.edge.FileChannel createChannel(final File file) {
final FileChannel channel = null;
try {
channel = new FileInputStream(file).getChannel();
channel.position(0);
FileChannel res = new FileChannelImpl(channel);
channel = null;
return res;
} catch (FileNotFoundException e) {
throw new VlcRuntimeException("Failed to open file: " + file, e);
} catch (IOException e) {
throw new VlcRuntimeException(e);
} finally {
if (channel != null) {
try {
channel.close();
} catch (...) {
...
}
}
}
}
跟进很久以后
鉴于文件句柄泄漏已被排除在外,我的下一个理论是服务器端实际上是在使用 Thread.interrupt()
中断它自己的线程。一些低级 I/O 调用通过抛出异常来响应中断,这里抛出的根异常看起来就像这样一个异常。
这并不能解释为什么会发生这种情况,但我粗略地猜测,这是服务器端框架试图解决过载或死锁问题。
关于Java Gridgain 应用程序在压力测试 1 天后开始失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1605522/