太多打开的文件错误,java.io.FileNotFoundException

5
在我的程序中,我有一个循环,它扫描一堆文件并读取它们的内容。问题发生在大约1500个文件的迭代过程中,似乎无法重现(或者我无法理解)。问题是:
java.io.FileNotFoundException: /path/to/file//myFile (Too many open files)

异常指向此方法:

private static String readFileAsRawString(File f) throws IOException {

    FileInputStream stream = new FileInputStream(f); // <------------Stacktrace
    try{
      FileChannel fc = stream.getChannel();
      MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());

      return Charset.defaultCharset().decode(bb).toString();
    } finally {
        stream.close();
    }
}

我在QA中对20000个文件运行了这个方法,看起来没有问题。

你看到上面我粘贴的代码有什么问题可能导致这个问题吗?


你在两个环境(QA和失败的那个)中使用了相同的操作系统吗? - SJuan76
4
“Finally”在返回之前被调用,我相信。 - James Raitsev
2
我会尝试关闭 FileChannel - 获取文件的通道可能会创建一个新的文件句柄。 - millimoose
此外,你的操作系统是否提供了一种方法来告诉我们一个进程打开了哪些文件?我知道在Windows上有一些任务管理器应用程序(例如SysInternals)可以做到这一点,因此值得查看确切打开了什么以确定原因。 - millimoose
@millimoose,是的,我也做了那个更改,还清理了一下方法。周一会进行测试。 - James Raitsev
显示剩余6条评论
3个回答

3
映射存在问题。一个MappedByteBuffer可以超过它的FileChannel存在,并且只有在垃圾收集时才会失效。您可能发现没有足够的垃圾运行GC,但在特定平台上,未引用的缓冲区可能会保留文件句柄。
除非明确禁用垃圾回收( -XX:-DisableExplicitGC),否则您应该通过捕获异常、调用System.gc()并再次尝试来测试它。如果第二次尝试成功了,那就是问题所在。然而,将System.gc()作为永久解决方案是不可取的。总体而言,最好的解决方案将需要针对目标平台进行一些分析。

我关心的是我无法在我的机器上(MAC)重现这个问题,我会在周一验证它在生产环境中的表现(Linux)。 - James Raitsev

2

不要使用MappedByteBuffer来执行这个简单的任务。它们被释放的时间没有明确定义。只需打开文件,读取它,然后关闭。


Apache的FileUtils.readFileToString(f);看起来代码更少,解决方案更干净。将在周一进行测试。 - James Raitsev

-1

我认为你打开文件的速度太快了,尝试添加一个wait()函数来测试一下。 然后添加一个静态计数器来跟踪已经打开的文件数量,如果已经打开了很多文件,就添加一个等待机制...


1
为什么?速度与此有何关系,等待又有什么作用?未经解释的猜测不是答案。-1。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接