29

我正在尝试在 Linux 上的 Jetty 7.0.1 中运行的 Java webapp 中调试文件描述符泄漏。

该应用程序已经愉快地运行了一个月左右,但由于打开的文件过多,请求开始失败,不得不重新启动 Jetty。

java.io.IOException: Cannot run program [external program]: java.io.IOException: error=24, Too many open files
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:459)
    at java.lang.Runtime.exec(Runtime.java:593)
    at org.apache.commons.exec.launcher.Java13CommandLauncher.exec(Java13CommandLauncher.java:58)
    at org.apache.commons.exec.DefaultExecutor.launch(DefaultExecutor.java:246)

起初我认为问题出在启动外部程序的代码上,但它使用的是commons-exec,我看不出有什么问题:

CommandLine command = new CommandLine("/path/to/command")
    .addArgument("...");
ByteArrayOutputStream errorBuffer = new ByteArrayOutputStream();
Executor executor = new DefaultExecutor();
executor.setWatchdog(new ExecuteWatchdog(PROCESS_TIMEOUT));
executor.setStreamHandler(new PumpStreamHandler(null, errorBuffer));
try {
    executor.execute(command);
} catch (ExecuteException executeException) {
    if (executeException.getExitValue() == EXIT_CODE_TIMEOUT) {
        throw new MyCommandException("timeout");
    } else {
        throw new MyCommandException(errorBuffer.toString("UTF-8"));
    }
}

列出服务器上打开的文件我可以看到大量的 FIFO:

# lsof -u jetty
...
java    524 jetty  218w  FIFO        0,6      0t0 19404236 pipe
java    524 jetty  219r  FIFO        0,6      0t0 19404008 pipe
java    524 jetty  220r  FIFO        0,6      0t0 19404237 pipe
java    524 jetty  222r  FIFO        0,6      0t0 19404238 pipe

Jetty 启动时只有 10 个 FIFO,几天后就有数百个。

我知道在这个阶段有点含糊,但是您对下一步看哪里有什么建议,或者如何获得有关这些文件描述符的更详细信息?

4

6 回答 6

26

问题来自您的 Java 应用程序(或您正在使用的库)。

首先,您应该阅读整个输出(Google for StreamGobbler),然后马上!

Javadoc说:

父进程使用这些流向子进程提供输入并从子进程获取输出。由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能会导致子进程阻塞,甚至死锁。

其次waitFor()你的进程要终止。然后您应该关闭输入、输出和错误流。

最后 destroy()你的过程。

我的消息来源:

于 2011-05-11T12:22:42.963 回答
7

当您在 Linux 上运行时,我怀疑您的文件描述符用完了。查看 ulimit。这是描述该问题的文章:http ://www.cyberciti.biz/faq/linux-increase-the-maximum-number-of-open-files/

于 2010-01-11T20:38:24.093 回答
7

除了调查文件泄漏等根本原因问题之外,为了合法地增加“打开文件”限制并使其在重新启动后持续存在,请考虑编辑

/etc/security/limits.conf

通过添加这样的东西

jetty soft nofile 2048
jetty hard nofile 4096

在这种情况下,“jetty”是用户名。有关 limits.conf 的更多详细信息,请参阅http://linux.die.net/man/5/limits.conf

注销然后重新登录并运行

ulimit -n

以验证更改是否已发生。此用户的新流程现在应该符合此更改。这个链接似乎描述了如何对已经运行的进程应用限制,但我没有尝试过。

对于大型 Java 应用程序,默认限制 1024 可能太低。

于 2014-07-31T17:10:08.760 回答
5

不知道您的应用程序的性质,但由于连接池泄漏,我已经多次看到此错误,因此值得一试。在 Linux 上,套接字连接使用文件描述符以及文件系统文件。只是一个想法。

于 2010-01-11T20:53:40.940 回答
1

您可以自己处理 fds。java 中的 exec 返回一个 Process 对象。间歇性地检查进程是否仍在运行。一旦它完成关闭进程 STDERR、STDIN 和 STDOUT 流(例如 proc.getErrorStream.close())。这将减少泄漏。

于 2011-03-24T13:24:49.520 回答
-1

当您同时在多个文件中写入数据并且您的操作系统具有固定的打开文件限制时,就会出现此问题。在 Linux 中,您可以增加打开文件的限制。

https://www.tecmint.com/increase-set-open-file-limits-in-linux/

如何更改 Linux 中打开文件的数量限制?

于 2018-06-06T12:43:07.887 回答