Process Builder的waitFor()问题和打开文件限制

9

我继承了一些代码:

Process p = new ProcessBuilder("/bin/chmod", "777", path).start();
p.waitFor();

基本上,有一些古老的、高度基于巫术的原因,将键/值对作为文件存储在磁盘上。我不想深入讨论。

然而,我却遇到了一堆IO异常:

Exception :Cannot run program "/bin/chmod": java.io.IOException: error=24, Too many open files
Message: Cannot run program "/bin/chmod": java.io.IOException: error=24, Too many open files

当我说一堆时,我指的是10k到数百万个。

我感觉waitFor调用是为了防止这些异常的发生,等待进程完成并退出,但是我认为chmod在文件实际关闭之前就返回了结果。有人知道这是否会导致这些异常吗?

我另一个想法是,在Java端打开和关闭成千上万个文件不够快,可能还有其他问题,比如在fw.close()被调用时某种形式的文件缓冲区没有被清除。

我对Java相当新手,这个问题让我困惑不解。 (幸运的是,该应用程序仍然可以运行..尽管输出了一个非常大的日志文件)

还有其他方法可以解决这个问题吗,例如清除缓冲区或将文件打开限制增加到JVM可以跟上自己的速度(假设这是问题所在)?


你的目标操作系统是什么(以及版本)?参考这个链接:http://unix.derkeiler.com/Newsgroups/comp.unix.solaris/2007-02/msg00873.html - Ryan Fernandes
Debian,看起来已经清除了uname。将会是最新的稳定版。 - lsl
4个回答

15

我假设你正在循环中运行这些chmod命令,否则我不明白为什么会出现这么多异常。可能是因为您没有读取生成进程的输出而导致死锁发生。在以前的ProcessBuilder, Runtime.exec()的日子里,这确实曾经困扰过我。

将您的代码片段更改为上述模式:

try {
    ProcessBuilder pb = new ProcessBuilder("/bin/chmod", "777", path);    
    pb.redirectErrorStream(true); // merge stdout, stderr of process

    Process p = pb.start();
    InputStreamReader isr = new  InputStreamReader(p.getInputStream());
    BufferedReader br = new BufferedReader(isr);

    String lineRead;
    while ((lineRead = br.readLine()) != null) {
        // swallow the line, or print it out - System.out.println(lineRead);
    }

    int rc = p.waitFor();
    // TODO error handling for non-zero rc
}
catch (IOException e) {
    e.printStackTrace(); // or log it, or otherwise handle it
}
catch (InterruptedException ie) {
    ie.printStackTrace(); // or log it, or otherwise handle it
} 

(参考:这个网站),看看是否能帮助解决问题。


尝试过了,仍然出现相同的异常。 - lsl
我认为我已经解决了它,请在几分钟后检查我的答案 - 只是等待测试来验证。 - lsl
好的,我选择你的答案,因为它在解决方案中需要这个,检查我的帖子将包括所需的额外行。 - lsl

8

感谢大家的帮助,这将可以解决其他地方因此而发生的许多奇怪问题。

使用你(Vinay)提供的示例和流关闭:

try{ 
  fw.close();

  ProcessBuilder pb = new ProcessBuilder("/bin/chmod", "777", path);

  pb.redirectErrorStream(true); // merge stdout, stderr of process
  p = pb.start();

  InputStreamReader isr = new  InputStreamReader(p.getInputStream());
  BufferedReader br = new BufferedReader(isr);

  String lineRead;
  while ((lineRead = br.readLine()) != null) {
    // swallow the line, or print it out - System.out.println(lineRead);
  }

} catch (Exception ioe) {
  Logger.logException(Logger.WARN, ioe.getMessage(), ioe);
} finally {
  try {
    p.waitFor();//here as there is some snipped code that was causing a different
                // exception which stopped it from getting processed

    //missing these was causing the mass amounts of open 'files'
    p.getInputStream().close();
    p.getOutputStream().close();
    p.getErrorStream().close(); 

  } catch (Exception ioe) {
    Logger.logException(Logger.WARN, ioe.getMessage(), ioe);
  }
}

这个想法来自John B Mathews的帖子


注意:仍然不明白为什么使用waitFor和关闭输入流就不能很好地完成,但我猜这就是Java的特点... - lsl
1
很好的发现,Jim,但是我还是看到了你finally中的问题。我认为你需要在各自的catch中调用每个close,否则如果在执行p.getInputStream.close()时出现异常,你将无法关闭其他流。这个问题现在可能已经消失了,但是以后可能会再次出现。 - Vinay Sajip
即使使用了“redirectErrorStream”合并输入和错误流,您是否真的需要关闭两个流?而且,即使您从未使用过输出流,您是否真的需要关闭它? - Rob Kennedy
你需要测试两者并找出答案。这篇文章很陈旧,我认为代码已经不再有效了。 - lsl

0

如果不关闭文件,进程似乎不太可能真正完成。这可能发生在非常多的线程中吗?或者有些线程实际上没有完成(即,在某些情况下它挂起了 waitFor)。

否则,我认为你将被卡住增加打开文件限制。假设这是类Unix系统,则“ulimit”命令可能是您要寻找的命令。


0
如果您正在使用JAVA 6,您也可以尝试在文件对象上使用新的设置器(用于读取、写入、执行)。可能会慢一些,但应该可以工作。

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