Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

How to deal with subprocesses in Java

Example of jps output

Once upon a time a hipster was sitting in a Starbucks, sipping their third Venti Something Coffee and pondering the meaning of life and the mysteries of the universe. After all, the day-job of hacking on a financial enterprise app was only providing a few challenges here and there, oh and a stable source of income. The ‘never satisfied’ entrepreneurial soul of our hero was always on the lookout for the next big thing. Before we could say “add a vanilla shot”, the next task popped into the notification area looking to be implemented by our hero.

Can we periodically run iostat, to check whether an application should self-tune based on the current CPU load?

‘Sure we can’ was the thought that appeared in the brilliant mind of our hero and no doubt yours.

Wait, what? Why would we ever need that? There are other tools to achieve the same result, including monitoring tools, plain timestamp collection to check how long operations take, JMX and so forth if nothing else works.

OK, put down the rotten vegetables you were ready to throw at me. Let’s not ruin the narrative we’ve just created. Let’s roll with the task as it is.

In this post, we’ll look at what means are there to execute shell commands from your Java code, how to run and govern child subprocesses, what libraries are there to help with this and what improvements to the process API are awaiting us in the sweet JDK 1.9.

Running processes like our forefathers did

Good thing about such a mature platform like Java is, most of the batteries are already included. The Process class has been in the JDK since the beginning of time, namely from the 1.0. However, subprocesses is one of the things where Write Once Run Anywhere principle is getting pushed to its limits. The underlying operating system treats processes differently and these differences inevitably spill into the Java world.

Nonetheless there are abstractions that unify the common functionality of external processes and offer the programmer values about the process lifecycle that are required.

The most obvious way to create a subprocess would be to exploit the Runtime.getRuntime().exec(“command”) method. If we would like to run the iostat to check the CPU load we would invoke something similar to the following code:

Process exec = Runtime.getRuntime().exec(new String[] { "iostat", "-C" });

The code snippet does in fact work well for some cases. It runs the iostat command and waits until it’s finished using thread sleep, 100 ms at a time. At the end it obtains the exit value of the subprocess, which usually indicates if the process has finished successfully or encountered any errors. All good programs will return 0 as a sign of success.

However, there are multiple problems with this code as it stands. First of all, and the most importantly, the output of the process must go somewhere. If it is becomes larger than some internal buffer size, it will not fit and cannot be handled automatically by the JVM. Developer intervention is required to deal with it, but unfortunately, there are no warning signs at all. So your program will eventually fill the buffer and stall until it is read by someone.

Trust me, you don’t want to debug these things in a large system. The problem is harder than it looks because you rarely run one process of a kind, but hundreds instead and figuring out which is which is not easy.

Good thing is that there’s an easy solution to this. First of all, let’s not use the archaic Runtime.exec(). Since Java 5, we have a marvellous ProcessBuilder class to configure subprocess executions using a fluid and more mature API.

Here is the corresponding code to the snippet we saw earlier but using the ProcessBuilder API.

Process iostat = new ProcessBuilder().command("iostat", "-C").inheritIO().start();
int exitCode = iostat.waitFor();
System.out.println("exitCode = " + exitCode);

Note the sneaky inheritIO() call in our code above. It takes the standard output of the executing command and pipes that to the standard output of the current Java program. It also does the same with the error stream of the subprocess.

So running the code above leads to the following output on my machine:

          disk0       cpu     load average
    KB/t tps  MB/s  us sy id   1m   5m   15m
   19.34  21  0.39   5  3 92  1.94 1.96 1.84
exitCode = 0

If you’re wondering what this output means, it’s saying that around 2 CPUs are currently busy. Here’s a good starting point article to dive into about understanding load average.

But we got distracted, now we know how to redirect subprocess IO into the main program’s output. Even more useful is the ability to redirect the output into whatever destination we want. A file, a logger, a NullInputStream… the redirect methods that you see on a screenshot below, like redirectError() or redirectInput(), or redirectOutput() give you total control over the subprocess streams.

Java Process redirect methods list

A note of caution, these methods first appeared in Java 1.7, so if you stumbled on the problem and are running something older it’s a really-really-really good time to upgrade. Also, I’m not sure if these methods are available when you’re using Android. Some Java 7 features are supported there, but I’m not aware about subprocesses. If you have the information, I’ll appreciate a good article about that.

Now another good thing that came into existence recently is time limited waiting for a process to finish. I hope we all agree that waiting on the process, in a blocking fashion, that is not guaranteed to finish is not the safest and most intelligent thing to do. Luckily, Java 1.8 has added a method, for just this eventuality.

boolean finished = iostat.waitFor(100, TimeUnit.MILLISECONDS);

Now, you can never stall for too long, waitFor(long timeout, TimeUnit unit) will return true or false depending on whether the process has finished in the allocated time. Throwing a timeout exception might be also a good idea, but that’s a matter of coding preference more than anything and the boolean return value is sufficient for all cases.

At this point in this post we know how to start a child process, handle its IO streams and wait for it to finish. If the process encounters an error and exits early, we can detect it with the same combo of the waitFor and exitValue methods.

However, if a child process never exits, we have a problem on our hands. We are required to deal with this reckless child and make it behave. There’s often no need to create a complicated solution to this problem though, as you can conveniently use the Avada Kedavra curse you learnt during dark arts at school. Also, this is probably the only appropriate situation to apply such logic.

Killing a process is a tricky business though. As everything is external to the JVM, it is bound to the underlying operating system laws so this could pose more complications that just calling a regular method.

The Process API offers the destroy() and destroyForcibly() methods, which apply the appropriate platform specific process stopping procedures. In my experience a utility method that kills a subprocess by its process ID (PID) is also a good idea.

Other useful methods on the ProcessBuilder that you should be aware of are directory() and environment(). The directory method allows you to specify the execution directory of the subprocess. The default execution directory is the same where your main Java program operates.

The environment() straightforwardly gives you the means to modify the execution environment variables your subprocess will have access to. By default these are inherited from the parent Java program and usually contain quite sensible values.

What will this look like when JDK 1.9 lands

The JDK will slowly but surely get better and better with every release. One JEP that targets Java 9 and will be implemented in the upcoming future is about improving process handling API. Here it is, JEP 102.

It won’t be a groundbreaking update, but it will make subprocess management easier. Currently, one of the major pain points is determining the PID, of the child process. PID is the universal identifier of the process that many commands and utilities know and understand. As for now, there is no general way to figure out the PID of the subprocess you started and the management functionality is limited to what Java API offers.

If you’re running Java processes, you can always fall back to using jps, a command line tool bundled with the JDK that can list currently running Java processes. Parsing jps output is quite straightforward, but requires some tinkering. For an example of using jps, refer to our post on JDK tools or dive into the official documentation. However, with Java 9, this should become much easier.

Another major improvement is managing the whole trees of subprocesses, if your program spawns additional programs which in turn create other children. Stopping the entire tree is a tricky job. I’m looking forward to seeing what the implementation will look like when the updates land.

Until then, the only thing I can recommend to use is the external libraries that deal with subprocesses like Apache commons-exec or our own zt-exec available under Apache 2.0 license. These will make your life easier, allow you to run and handle processes asynchronously as well as other bells, whistles and useful APIs.


In this post we looked at the Process API that is offered by the JDK to start and manage subprocesses. Where and how you can redirect process output streams and how to determine if the process has finished its work.

In the future Java 9 release, process management functionality will be improved, however, it won’t be life-changing for your current applications that deal with spawning subprocesses. It will mostly target the managemental side of things like getting to know PIDs, dealing with trees of processes and so forth.

How often do you have to deal with spawning a process in your typical projects? Is Java API enough for what you do? Leave a comment below or find and chat with me on Twitter: @shelajev.