Crash, Boom: Too Many Open Files…

We’ve been plagued by an error similar to this in some long-running server code:

Boom!

Exception in thread “main” java.io.IOException: Cannot run program “…whatever”: error=24, Too many open files

I am pretty sure I tracked down the problem: failure to correctly close Process resources. Consider the case where you create a Process and read data from its InputStream:

(note: this is not a complete example, plus it is wrong!)

// same thing occurs with Runtime.exec(...)
ProcessBuilder pb = new ProcessBuilder(args);
Process proc = pb.start();
InputStream in = proc.getInputStream();
// ...read from the stream in a thread
proc.waitFor();

// ...later, in a finally block, close the InputStream:
in.close();

That’s the general idea. Create a Process, capture output from one or more of the streams, and close the streams when complete.

Unfortunately, this is wrong.

Poor Documentation

The documentation does not really help much. For instance, here is what the comment says for the Process.destroy() method:

Kills the subprocess. The subprocess represented by this Process object is forcibly terminated.

Hmm…that doesn’t really say how the streams are handled. I’ve always assumed if I used one of the streams, I had to close the one I used.

Here are three bug reports asking for documentation clarification:

After studying these reports, I now feel confident that you must first close all three streams, then you must call destroy() in order to fully reclaim resources.

Example Code

Here is a simple example that shows what I’m talking about. Despite the fact that I do not actually use any of the streams, I still need to close them:

public class ProcDemo {
  public static void main(String[] args) throws
        InterruptedException, IOException {
    Process proc = null;
    try {
      ProcessBuilder pb = new ProcessBuilder(args);
      proc = pb.start();
      proc.waitFor();
    } finally {
      if (proc != null) {
        close(proc.getOutputStream());
        close(proc.getInputStream());
        close(proc.getErrorStream());
        proc.destroy();
      }
    }
  }

  private static void close(Closeable c) {
    if (c != null) {
      try {
        c.close();
      } catch (IOException e) {
        // ignored
      }
    }
  }
}

If you study the code for java.lang.ProcessImpl, you’ll see that when you construct the Process object, all three streams are created right away. Thus, regardless of whether you use a stream, you must close all of them.

And even though the Javadoc for destroy() is not all that clear, from reading the bug reports, it is pretty clear you should call it as well.

Problem Solved?

It will probably be several days before I know if this truly solves our problem. While I was able to clean up all of the code I wrote, I don’t have much control over the various libraries we use. Does JGroups always close Process resources correctly? How about Svnkit? We often see these “Too many open files” errors immediately after a large Subversion checkout, so I am somewhat skeptical.


Jason Sankey Says:

Hey Eric,

Ah yes, a familiar problem :) . I agree the documentation is very poor here. The safest solution is as you suggest to close all streams yourself and call destroy. Looking into the JDK source, I see at least on Unix it will almost certainly close the streams itself in destroy() (it does them all in one try/catch which is a bit sloppy – what if the first close() fails?).

More specifically, we also saw this problem with Pulse at a customer site using Subversion and OSX. I eventually tracked the problem to SvnKit and reported it with a patch: I believe the patch has been applied now in their svn repo.

Sam.Halliday Says:

What happens if the stdout really is System.out? Doesn’t that close it? I’ve seen a bug where a Logger closes stderr and then nothing else can write to it in the entire application.

Martin.Smith Says:

simply using destroy() fixed the too many open files for me.
How convenient this was posted the same day I ran into this error !!