Tag Archives: pitfall

Java pitfall: How to prevent Runtime.getRuntime().exec() from hanging

Runtime.getRuntime().exec() is used to execute a command line program from within the Java program as below.

import java.io.File;
import java.io.IOException;

public class ProcessExecutor {

    public static void main(String[] args) throws IOException, InterruptedException {

        String command = "c:\my.exe";
        String workingDir = "c:\myworkingdir";

        // start execution
        Process process = Runtime.getRuntime().exec(command, null, new File(workingDir));

        // wait for completion
        process.waitFor();

    }

}

However the command line program being run above may block/deadlock as it did for me on Windows 7. I was trying to run a program that produced a lot of output. I could run the program standalone but through Java it hung indefinitely. Thread dumps showed nothing.

After being quite puzzled for a while as to why this was happening finally I found the answer in Java 7 api docs for Process.

Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.

So, in fact, the fix for the above program is as follows.

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;

public class ProcessExecutor {

    public static void main(String[] args) throws IOException, InterruptedException {

        String command = "c:\my.exe";
        String workingDir = "c:\myworkingdir";

        // start execution
        Process process = Runtime.getRuntime().exec(command, null, new File(workingDir));

        // exhaust input stream
        BufferedInputStream in = new BufferedInputStream(process.getInputStream());
        byte[] bytes = new byte[4096];
        while (in.read(bytes) != -1) {}

        // wait for completion
        process.waitFor();

    }

}

This is so bad. Not only is this unexpected but it is also undocumented in the exec call. Also another problem is that if you are timing the total execution time for a given command and don’t care about the output you need to read the output anyway and probably subtract the reading time from the total execution time. I’m not sure how accurate that will be.

Surely there could have been a better way to handle this for the user in the api internals. So windows 7 must be one of those OSs with small buffer sizes then. Anyway, at least you know now. Obviously you don’t have to read it into nothing as I’m doing above. You can write it to stdout or a file.

Update: A commenter made a good point that I’d forgotten to read the error stream above. Don’t forget to do so in your own code!