Fasterj

|home |articles |cartoons |site map |contact us |
Tools: | GC log analysers| Multi-tenancy tools| Books| SizeOf| Thread analysers| Heap dump analysers|

Our valued sponsors who help make this site possible
JProfiler: Get rid of your performance problems and memory leaks! 

Training online: Concurrency, Threading, GC, Advanced Java and more ... 

Java 26 Process AutoCloseable: Cleaner Process Execution

JProfiler
Get rid of your performance problems and memory leaks!

Modern Garbage Collection Tuning
Shows tuning flow chart for GC tuning


Java Performance Tuning, 2nd ed
The classic and most comprehensive book on tuning Java

Java Performance Tuning Newsletter
Your source of Java performance news. Subscribe now!
Enter email:



JProfiler
Get rid of your performance problems and memory leaks!


In this article I explain how Java 26 simplifies Process resource management

Published February 2026, Author Jack Shirazi

Process is now AutoCloseable in Java 26

In Java 26 with JDK-8364361, java.lang.Process has been updated so it implements AutoCloseable/Closeable with a new close() method. A process can now be used in try-with-resources and closed automatically at scope exit.

Closing a process ensures that it has terminated and its streams and underlying resources are released. When used with try-with-resources, the process and its streams are closed when the try-with-resources block exits.

For many of you this is all the information you need, and you can stop reading now. The rest of this article shows an example of good generic process handling code, and how that can be simplified by Java 26

Up to Java 25: Process execution with manual cleanup

The following example wraps process execution to:

The code in bold is what changes for Java 26 - most of that can be eliminated entirely, resulting in simpler and likely safer code.

  private volatile int exitValue;

public StringBuilder executeCommand(int timeoutInSeconds, String[] command, Map<String, String> envs) throws IOException { try { exitValue = -1; System.out.println("Trying to execute with timeout = " + timeoutInSeconds + " seconds, command = " + Arrays.toString(command)); ProcessBuilder pb = new ProcessBuilder(command); if (envs != null) { Map<String, String> env = pb.environment(); for (String setEnv : envs.keySet()) { env.put(setEnv, envs.get(setEnv)); } } // Merge stdout and stderr so I only have to read one stream. pb.redirectErrorStream(true);

// Set up infrastructure for the process management StringBuilder commandOutput = new StringBuilder(); long timeout = timeoutInSeconds * 1000L; long start = System.currentTimeMillis(); long now = start; boolean isAlive = true; byte[] buffer = new byte[64 * 1000];

Process p = pb.start(); //can't be in the try-block

try (InputStream in = p.getInputStream()) { // Stop trying if the time elapsed exceeds the timeout. //stop trying if the time elapsed exceeds the timeout while (isAlive && (now - start) < timeout) { while (in.available() > 0) { int lengthRead = in.read(buffer, 0, buffer.length); commandOutput.append(new String(buffer, 0, lengthRead)); } try {Thread.sleep(1000L);} catch (InterruptedException e) {Thread.currentThread().interrupt();} now = System.currentTimeMillis(); // If it's not alive but there is still readable input, then continue reading. isAlive = p.isAlive() || in.available() > 0; } }

//Cleanup as well as I can. All this bold part is unnecessary from Java 26 (unless explicit shutdown handling is desired) boolean exited = false; try {exited = p.waitFor(3, TimeUnit.SECONDS);}catch (InterruptedException e) {Thread.currentThread().interrupt();} if (!exited) { p.destroy(); try {Thread.sleep(1000L);} catch (InterruptedException e) {Thread.currentThread().interrupt();} if (p.isAlive()) { p.destroyForcibly(); try {p.waitFor(3, TimeUnit.SECONDS);}catch (InterruptedException e) {Thread.currentThread().interrupt();} } } if (!p.isAlive()) { exitValue = p.exitValue(); } if ((now - start) > timeout) { throw new TimedOutException("Timed out: timeout of "+timeoutInSeconds+" seconds, command = "+Arrays.toString(command)); } return commandOutput; } catch (TimedOutException e) { // Needed as TimedOutException is preferable to the more generic CommandFailedException throw e; } catch (IOException e) { throw new CommandFailedException("Command failed, command = "+Arrays.toString(command), e); } }

From Java 26, Process cleanup can be automatic

The same example as above but simplified to use the Java 26 Process AutoCloseable feature.

  private volatile int exitValue;

public StringBuilder executeCommand(int timeoutInSeconds, String[] command, Map<String, String> envs) throws IOException { try { exitValue = -1; System.out.println("Trying to execute with timeout = " + timeoutInSeconds + " seconds, command = " + Arrays.toString(command)); ProcessBuilder pb = new ProcessBuilder(command); if (envs != null) { Map<String, String> env = pb.environment(); for (String setEnv : envs.keySet()) { env.put(setEnv, envs.get(setEnv)); } } // Merge stdout and stderr so I only have to read one stream. pb.redirectErrorStream(true);

// Set up infrastructure for the process management StringBuilder commandOutput = new StringBuilder(); long timeout = timeoutInSeconds * 1000L; long start = System.currentTimeMillis(); long now = start; boolean isAlive = true; byte[] buffer = new byte[64 * 1000];

// Java 26: Process is AutoCloseable/Closeable. The start() is moved into the try block try (Process p = pb.start(); InputStream in = p.getInputStream()) { // Stop trying if the time elapsed exceeds the timeout. while (isAlive && (now - start) < timeout) { while (in.available() > 0) { int lengthRead = in.read(buffer, 0, buffer.length); commandOutput.append(new String(buffer, 0, lengthRead)); } try {Thread.sleep(1000L);} catch (InterruptedException e) {Thread.currentThread().interrupt();} now = System.currentTimeMillis(); // If it's not alive but there is still readable input, then continue reading. isAlive = p.isAlive() || in.available() > 0; }

if (!p.isAlive()) { exitValue = p.exitValue(); } if ((now - start) > timeout) { throw new TimedOutException("Timed out: timeout of " + timeoutInSeconds + " seconds, command = " + Arrays.toString(command)); } return commandOutput; } } catch (TimedOutException e) { // Needed as TimedOutException is preferable to the more generic CommandFailedException throw e; } catch (IOException e) { throw new CommandFailedException("Command failed, command = " + Arrays.toString(command), e); } }

Bottom line

The Java 26 change does not replace execution logic, timeout checks, or error mapping. It mainly simplifies process resource management by letting the language handle close/cleanup at block exit


Last Updated: 2026-02-26
Copyright © 2007-2026 Fasterj.com. All Rights Reserved.
All trademarks and registered trademarks appearing on Fasterj.com are the property of their respective owners.
URL: http://www.fasterj.com/articles/java26-process-autoclose.shtml
RSS Feed: http://www.JavaPerformanceTuning.com/newsletters.rss
Trouble with this page? Please contact us