【问题标题】:Too many open files: how many are open, what they are, and how many can the JVM open打开的文件太多:打开了多少,它们是什么,JVM可以打开多少
【发布时间】:2010-02-16 12:45:16
【问题描述】:

我在 Java 中遇到了这个异常:

java.io.FileNotFoundException: (Too many open files) 

我正在寻找解决此问题的方法。

这个错误显然表明 JVM 分配了太多句柄,而底层操作系统不会让它有更多。要么我在连接/流关闭不正确的地方出现泄漏。

此过程连续运行数天并最终引发异常。它在正常运行 12-14 天后反复发生。

你是怎么解决这个问题的?有没有办法在 JVM 中获取已分配句柄的列表或跟踪它何时达到一定数量?我很想把它们打印出来,看看它是如何生长的以及何时生长的。我不能使用分析器,因为它是一个生产系统并且在开发中难以重现它。有什么建议吗?

我正在监视空闲堆大小并在它接近 -Xmx 中指定的总数的 1% 时发出“警报”。我也知道,如果我的线程数达到 500 以上,那么肯定会失控。现在,有没有办法知道我的 JVM 从 OS 分配了 太多的句柄,并且没有返回它们,例如套接字、打开的文件等。如果我知道这些,我就会知道在哪里以及何时查看。

【问题讨论】:

  • 你能提供更多关于JVM和操作系统的信息吗?另外,我建议您在开发环境中投入更多精力来重现这一点。这可能很麻烦,但是当您遇到此类问题时,尝试在生产系统上“观察和报告”可能需要更长的时间。
  • 在运行虚拟化的 Linux 上确实会反复发生这种情况,我们真的无法通过运行 2 周的重负载测试来重现这种情况。不过,我还没有在 Windows 盒子上看到这种情况。没错,最好在 dev. 中捕获它,但我也希望在服务器本身中嵌入一些功能,以便将来进行自我监控。
  • 重新编辑。我认为 JVM 无法告诉您它是如何打开任何文件的。这可能是 Sun 添加的一个不错的功能,但在此之前,您将不得不使用外部进程来告诉您。如果您在 JVM 中确实需要它,请编写一些执行 lsof 并返回结果的代码。此外,可以更改打开文件的限制。例如在 Linux 中,您可以修改 /etc/security/limits.conf 文件。
  • bramp,谢谢.. linux 可以授予单个进程的默认最大 nof 句柄是多少?我查看了我的默认 ubuntu 安装和 limits.conf 如果几乎是空的,我认为它使用了一些默认值..
  • 回复我自己 - 默认为 1024

标签: java jvm


【解决方案1】:

你没有说你在哪个操作系统上运行,但如果你在 Linux 上运行,你可以使用 lsof 命令

lsof -p <pid of jvm>

这将列出 JVM 打开的所有文件。或者,如果您在 Windows 上运行,您可以 Process Explorer 显示所有进程的所有打开文件。

这样做有望让您缩小保持文件打开的代码位的范围。

【讨论】:

  • 好主意。检查您可以传递给“lsof”的标志,以便每 X 秒重复一次。
  • 我已经添加了关于 Linixes 的评论。感谢 lsof 的提示!
  • 我很想知道我的解决方案是否能帮助您找到问题所在。另外,如果不是保密或太麻烦,您能否解释一下最终导致问题的原因?
  • 我遇到了同样的问题,并且也明智地关闭了我的所有文件句柄,但会定期在lsof 中列出大约 10,000 个条目,man 页面说是 POSIX 信号量。任何线索 JDK 使用 PSXSEM 做什么? java 36809 smm 9907r PSXSEM 0t0 kcms00008FC901624000
  • lsof -p 也适用于我。我看到打开文件的列表不断增加,直到垃圾收集器运行或当我使用 jconsole 手动运行 GC 时。我考虑过做一个 System.gc() 但它大大增加了响应时间。
【解决方案2】:

由于您使用的是 Linux,我建议您检查 /proc 文件系统。在 proc 中,您将找到一个文件夹,其中包含您的进程的 PID,其中包含一个名为“fd”的文件夹。如果你的进程id是1234,路径是

/proc/1234/fd

在该文件夹中,您会找到所有打开文件的链接(执行“ls -l”)。通常,您可以通过文件名判断哪个库/代码可能会打开而不是关闭文件。

【讨论】:

  • 谢谢,这很有用,但我想要的是避免深入研究文件系统或操作系统的东西。我想让 JVM 进程告诉我它为什么“生病”,我想要它的原因是让用户头疼的是出了问题。
【解决方案3】:

所以,完整的答案(我结合了@phisch 和@bramp 的答案)。 如果要检查所有进程,则应使用sudo。将结果保存到文件也很好 - lsof 并不便宜 + 这个文件可能对进一步调查有用。

sudo lsof > lsof.log

显示坏人(@Arun 评论中的 UPDATE):

cat lsof.log | awk '{print $1 " " $2 " " $5}' | sort | uniq |awk '{ print $2 " " $1; }' | sort -rn | uniq -c | sort -rn | head -5

    2687 114970 java
    131 127992 nginx
    109 128005 nginx
    105 127994 nginx
    103 128019 nginx

将文件描述符列表也保存到文件中:

sudo ls -l /proc/114970/fd > fd.log

显示顶部打开的文件:

cat fd | awk '{ print $11 }' | sort -rn | uniq -c | sort -rn | head -n20

【讨论】:

  • 非常感谢,真的很有用!不过,我认为您忘记了第三个命令中的一部分:它应该是 sudo ls -l /proc/114970/fd &gt; fd.log
  • @ocramot,不客气! p.s.你是对的,我修正了我的答案。
  • 第一个命令是错误的,它只使用了 lsof 和 groups 。它为具有不同 tid 的同一 fd 提供了许多重复的文件句柄。我认为那些 tids 处于被杀状态,但 lsof 显示它,因为它仍然可以找到它。请更正此答案
  • 第一个命令应该如下。 code sudo lsof > lsof.log && cat lsof.log | awk '{打印 $1 " " $2 " " $5}' |排序 | uniq |awk '{ 打印 $2 " " $1; }' |排序-rn |唯一的-c |排序-rn |头 -20
  • @ArunGeorge 你能先解释一下awk + sort + uniq + 第二个awk吗?就我而言,我的命令运行良好(没有重复),但可能取决于环境/应用程序。
【解决方案4】:

您可以通过将以下内容添加到/etc/security/limits.conf 来更改打开文件的限制:

* soft nofile 2048 # Set the limit according to your needs
* hard nofile 2048

然后您可以在 shell 上使用 sysctl -p 重新加载配置。检查this article

为了完整起见,您可以使用以下命令验证打开文件的当前限制是多少:ulimit -n

【讨论】:

    【解决方案5】:

    如果您使用的是 MacOS

    sudo launchctl limit maxfiles <hard> <soft>
    sudo launchctl limit maxfiles 1024 200000
    

    【讨论】:

      猜你喜欢
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      • 2013-02-05
      • 2011-08-15
      • 2015-12-17
      • 2016-10-14
      • 1970-01-01
      相关资源
      最近更新 更多