在一个系统中程序每隔5分钟会通过ProcessBuilder创建进程执行shell脚本。系统运行一段时间后,会报以下错误:
java.io.IOException: Cannot run program “ssh”: java.io.IOException: error=24, Too many open files at java.lang.ProcessBuilder.start(ProcessBuilder.java:460)
通过排查发现是由于进程执行shell脚本后,没有再调用进程的destroy方法。修改后的程序如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26String cmd = "xxx";
Process process = null;
try {
//创建ProcessBuilder实例
ProcessBuilder processBuilder = new ProcessBuilder("ssh", "root@xxx.xxx.xxx", cmd);
processBuilder.redirectErrorStream();
//创建进程并启动
process = processBuilder.start();
//输入流
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
//读取进程返回的结果
while((line = input.readLine())!= null) {
//处理返回的结果
}
process.waitFor();
//关闭输入流
input.close();
} catch (Exception e) {
logger.error("execute [" + cmd + "] fail", e);
} finally {
//此处作了修改,调用进程的destroy方法销毁进程
if (process != null) {
process.destroy();
}
}
修改后系统运行不再报错。
关于此问题,具体原因如下:
Linux系统对于每个进程能打开的最大文件数有限制(一般为65535,可使用“ulimit -n”命令查看该值)。当通过ProcessBuilder创建进程时,STDIN、STDOUT、STDERR会和Java主进程通过输入、输出流建立管道连接,从而占用文件描述符,当进程执行结束后,若不关闭输入、输出流和销毁进程,则会造成Java主进程文件描述符的泄露。因此,需要在进程执行结束后,关闭输入、输出流,销毁进程。