1

所以我有一个在 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 个此类通道)。似乎在一段时间后它不再能够打开文件,我无法解释为什么会发生这种情况,并且非常感谢任何能告诉我可能导致这种情况以及如何进行跟踪的人它下来并修复它。如果它可能与文件句柄耗尽有关,我很想听听任何提示以确保确定...即在 JVM 运行时查询它或使用 linux 命令行工具来了解有关当前打开的句柄的更多信息.

更新:我一直在使用命令行工具来询问 lsof 的输出,并且无法看到任何文件句柄处于打开状态的证据......网格中的每个节点都有一个非常稳定的打开文件配置文件我可以看到在执行上述代码时发生了变化......但它总是返回到稳定数量的打开文件。

与此问题相关:释放 java 文件句柄

4

1 回答 1

2

有几种情况可能不会关闭文件句柄:

  1. 可能还有一些其他代码可以打开文件。
  2. 可能还有其他一些代码会调用createChannel(...)和不调用fillContactBuffer(...)
  3. 如果channel.position(0)抛出异常,通道将不会关闭。解决方法是重新排列代码,使以下语句位于try块内。

    channel.position(0);
    return new FileChannelImpl(channel);
    

编辑:查看堆栈跟踪,这两种方法似乎在不同的代码库中。我会把责任归咎于这种createChannel方法。即使它不是问题的根源,它也可能会泄漏。它需要一个 in internalfinally子句来确保在发生异常时关闭通道。

像这样的东西应该可以解决问题。请注意,您需要确保该finally块不会在成功时关闭通道!

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 调用通过抛出异常来响应中断,而这里抛出的根异常看起来就是这样一个异常。

这并不能解释为什么会发生这种情况,但我猜测这是服务器端框架试图解决过载或死锁问题。

于 2009-10-22T07:31:27.603 回答