java - 关于用 java 导致太多打开的文件?

标签 java ulimit

查看同事的代码时,发现如下代码

    BufferedReader br = new BufferedReader(new FileReader(PATH + fileName));
    //...

只是读取一个文件并将这些行连接为一行,但我没有找到任何关闭代码,所以我认为它应该会导致资源泄漏,并最终导致打开文件太多错误,所以为了证明这一点,我写了一个测试

for (int i = 0; i < 7168; i++) { // ulimit -n ==> 7168
    BufferedReader br = new BufferedReader(new FileReader("src/main/resources/privateKey/foo.pem"));
    System.out.println(br.readLine());
}
System.in.read();

很奇怪,一切正常,没有抛出预期的异常。

并在命令行查看真正打开的文件

➜  ~ lsof -p 16276 | grep 'foo.pem' | wc -l
    2538

为什么只有 2538 而不是 7168?

那怎么了?如何导致太多打开文件错误


按照@GhostCat的建议,改成7168 --> Integer.MAX_VALUE,这次导致了

java.io.FileNotFoundException: src/main/resources/privateKey/foo.pem (Too many open files in system)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)

当 i 为 27436 时,在这种情况下检查命令行中真正打开的文件是

➜  ~ lsof | grep foo.pem | wc -l
    7275

但是剩下的文件(27346 - 7275)在哪里?为什么 ulimit number 不起作用?

最佳答案

我假设垃圾收集器正在运行,发现许多无法访问的 BufferedReader 对象并收集它们。这会导致底层流对象被最终确定……从而关闭它们。

要打破此代码,请将 BufferedReader 对象添加到列表中,以便它们保持可访问性。


这就是我认为将 7168 更改为 MAXINT 有效的原因。

当 JVM 启动时,它会使用相对较小的堆。 GC 期间发生的一件事是 JVM 决定是否需要调整堆的大小。所以这就是可能发生的事情:

  • JVM 以一个太小的堆开始,无法容纳 7168 个打开的文件 + BufferedReader 对象。 (请记住,后者中的每一个都可能有一个预分配的缓冲区!)

  • 您开始打开文件。

  • 在大约 N = 7168 - 2538 时,堆填满了所有 BufferedReader 对象 + FileInputStream 对象 + 来自 JVM 启动/预热的各种碎屑。

  • GC 运行,并导致(可能)收集/完成/关闭所有 BufferedReader 对象。

  • 然后 GC 决定它需要扩展堆。您现在有足够的堆空间来容纳比您的 ulimit 允许的更多的打开的 BufferedReader 对象。

  • 您继续打开文件...然后达到打开文件限制。

这是一种可能的模式。


如果你真的想调查这个,我建议你打开 GC 日志记录,看看你是否可以将 lsof 报告的 FD 数量与 GC 运行相关联。

(您可以尝试在每个打开之间添加 sleep 调用,以便更容易获得 lsof 测量值。但这可能会以其他方式改变 JVM 行为...)

关于java - 关于用 java 导致太多打开的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43395025/

相关文章:

java - Java 中的字符串构建性能

java - 如何修复 Java webapp 中的地方性 XSS 漏洞

ruby - 为什么堆栈内存限制系统之间的差异很大?

ubuntu - 从 ubuntu 的终端,更改文件描述符编号的 ulimit

相当于 ulimit -n 的 Windows

java - JSP 表单正在选择 FireBug 中编辑的值

java - jtable更新数据不可见

node.js - 我可以从 node.js 设置 ulimit 吗?

c - `sysconf()` 和 `getrlimit()` 有什么关系和区别?

java - 一种将共享资源分配给多个节点中的一个节点的分布式算法