Skip to content

Commit afb4a1b

Browse files
author
Roger Riggs
committed
8354872: Clarify java.lang.Process resource cleanup
Reviewed-by: jpai
1 parent 7c13a2c commit afb4a1b

File tree

2 files changed

+178
-86
lines changed

2 files changed

+178
-86
lines changed

src/java.base/share/classes/java/lang/Process.java

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -41,7 +41,7 @@
4141

4242
/**
4343
* {@code Process} provides control of native processes started by
44-
* ProcessBuilder.start and Runtime.exec.
44+
* {@code ProcessBuilder.start} and {@code Runtime.exec}.
4545
* The class provides methods for performing input from the process, performing
4646
* output to the process, waiting for the process to complete,
4747
* checking the exit status of the process, and destroying (killing)
@@ -78,10 +78,6 @@
7878
* process I/O can also be redirected</a>
7979
* using methods of the {@link ProcessBuilder} class.
8080
*
81-
* <p>The process is not killed when there are no more references to
82-
* the {@code Process} object, but rather the process
83-
* continues executing asynchronously.
84-
*
8581
* <p>There is no requirement that the process represented by a {@code
8682
* Process} object execute asynchronously or concurrently with respect
8783
* to the Java process that owns the {@code Process} object.
@@ -98,6 +94,49 @@
9894
* Delegating to the underlying Process or ProcessHandle is typically
9995
* easiest and most efficient.
10096
*
97+
* <h2>Resource Usage</h2>
98+
* {@linkplain ProcessBuilder#start() Starting a process} uses resources in both the invoking process and the invoked
99+
* process and for the communication streams between them.
100+
* The resources to control the process and for communication between the processes are retained
101+
* until there are no longer any references to the Process or the input, error, and output streams
102+
* or readers, or they have been closed.
103+
*
104+
* <p>The process is not killed when there are no more references to the {@code Process} object,
105+
* but rather the process continues executing asynchronously.
106+
* The process implementation closes file descriptors and handles for streams
107+
* that are no longer referenced to prevent leaking operating system resources.
108+
* Processes that have terminated or been terminated are monitored and their resources released.
109+
*
110+
* <p>Streams should be {@code closed} when they are no longer needed, to avoid delaying
111+
* releasing the operating system resources.
112+
* {@code Try-with-resources} can be used to open and close the streams.
113+
* <p>For example, to capture the output of a program known to produce some output and then exit:
114+
* {@snippet lang = "java" :
115+
* List<String> capture(List<String> args) throws Exception {
116+
* ProcessBuilder pb = new ProcessBuilder(args);
117+
* Process process = pb.start();
118+
* try (BufferedReader in = process.inputReader()) {
119+
* List<String> captured = in.readAllLines();
120+
* int status = process.waitFor();
121+
* if (status != 0) {
122+
* throw new RuntimeException("Process %d: %s failed with %d"
123+
* .formatted(process.pid(), args, status));
124+
* }
125+
* return captured;
126+
* }
127+
* }
128+
* }
129+
* <p>Stream resources (file descriptor or handle) are always paired; one in the invoking process
130+
* and the other end of that connection in the invoked process.
131+
* Closing a stream at either end terminates communication but does not have any direct effect
132+
* on the other Process. The closing of the stream typically results in the other process exiting.
133+
*
134+
* <p> {@linkplain #destroy Destroying a process} signals the operating system to terminate the process.
135+
* It is up to the operating system to clean up and release the resources of that process.
136+
* Typically, file descriptors and handles are closed. When they are closed, any connections to
137+
* other processes are terminated and file descriptors and handles in the invoking process signal
138+
* end-of-file or closed. Usually, that is seen as an end-of-file or an exception.
139+
*
101140
* @since 1.0
102141
*/
103142
public abstract class Process {
@@ -127,6 +166,9 @@ public Process() {}
127166
* then this method will return a
128167
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
129168
*
169+
* <p>The output stream should be {@linkplain OutputStream#close closed}
170+
* when it is no longer needed.
171+
*
130172
* @apiNote
131173
* When writing to both {@link #getOutputStream()} and either {@link #outputWriter()}
132174
* or {@link #outputWriter(Charset)}, {@link BufferedWriter#flush BufferedWriter.flush}
@@ -159,9 +201,15 @@ public Process() {}
159201
* then the input stream returned by this method will receive the
160202
* merged standard output and the standard error of the process.
161203
*
204+
* <p>The input stream should be {@linkplain InputStream#close closed}
205+
* when it is no longer needed.
206+
*
162207
* @apiNote
163-
* Use {@link #getInputStream()} and {@link #inputReader()} with extreme care.
164-
* The {@code BufferedReader} may have buffered input from the input stream.
208+
* Use either this method or an {@linkplain #inputReader() input reader}
209+
* but not both on the same {@code Process}.
210+
* The input reader consumes and buffers bytes from the input stream.
211+
* Bytes read from the input stream would not be seen by the reader and
212+
* buffer contents are unpredictable.
165213
*
166214
* @implNote
167215
* Implementation note: It is a good idea for the returned
@@ -185,9 +233,15 @@ public Process() {}
185233
* then this method will return a
186234
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
187235
*
236+
* <p>The error stream should be {@linkplain InputStream#close closed}
237+
* when it is no longer needed.
238+
*
188239
* @apiNote
189-
* Use {@link #getErrorStream()} and {@link #errorReader()} with extreme care.
190-
* The {@code BufferedReader} may have buffered input from the error stream.
240+
* Use either this method or an {@linkplain #errorReader() error reader}
241+
* but not both on the same {@code Process}.
242+
* The error reader consumes and buffers bytes from the error stream.
243+
* Bytes read from the error stream would not be seen by the reader and the
244+
* buffer contents are unpredictable.
191245
*
192246
* @implNote
193247
* Implementation note: It is a good idea for the returned
@@ -208,6 +262,16 @@ public Process() {}
208262
* If the {@code native.encoding} is not a valid charset name or not supported
209263
* the {@link Charset#defaultCharset()} is used.
210264
*
265+
* <p>The reader should be {@linkplain BufferedReader#close closed}
266+
* when it is no longer needed.
267+
*
268+
* @apiNote
269+
* Use either this method or the {@linkplain #getInputStream input stream}
270+
* but not both on the same {@code Process}.
271+
* The input reader consumes and buffers bytes from the input stream.
272+
* Bytes read from the input stream would not be seen by the reader and the
273+
* buffer contents are unpredictable.
274+
*
211275
* @return a {@link BufferedReader BufferedReader} using the
212276
* {@code native.encoding} if supported, otherwise, the
213277
* {@link Charset#defaultCharset()}
@@ -238,16 +302,21 @@ public final BufferedReader inputReader() {
238302
* then the {@code InputStreamReader} will be reading from a
239303
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
240304
*
305+
* <p>The reader should be {@linkplain BufferedReader#close closed}
306+
* when it is no longer needed.
307+
*
241308
* <p>Otherwise, if the standard error of the process has been redirected using
242309
* {@link ProcessBuilder#redirectErrorStream(boolean)
243310
* ProcessBuilder.redirectErrorStream} then the input reader returned by
244311
* this method will receive the merged standard output and the standard error
245312
* of the process.
246313
*
247314
* @apiNote
248-
* Using both {@link #getInputStream} and {@link #inputReader(Charset)} has
249-
* unpredictable behavior since the buffered reader reads ahead from the
250-
* input stream.
315+
* Use either this method or the {@linkplain #getInputStream input stream}
316+
* but not both on the same {@code Process}.
317+
* The input reader consumes and buffers bytes from the input stream.
318+
* Bytes read from the input stream would not be seen by the reader and the
319+
* buffer contents are unpredictable.
251320
*
252321
* <p>When the process has terminated, and the standard input has not been redirected,
253322
* reading of the bytes available from the underlying stream is on a best effort basis and
@@ -283,6 +352,16 @@ public final BufferedReader inputReader(Charset charset) {
283352
* If the {@code native.encoding} is not a valid charset name or not supported
284353
* the {@link Charset#defaultCharset()} is used.
285354
*
355+
* <p>The error reader should be {@linkplain BufferedReader#close closed}
356+
* when it is no longer needed.
357+
*
358+
* @apiNote
359+
* Use either this method or the {@linkplain #getErrorStream error stream}
360+
* but not both on the same {@code Process}.
361+
* The error reader consumes and buffers bytes from the error stream.
362+
* Bytes read from the error stream would not be seen by the reader and the
363+
* buffer contents are unpredictable.
364+
*
286365
* @return a {@link BufferedReader BufferedReader} using the
287366
* {@code native.encoding} if supported, otherwise, the
288367
* {@link Charset#defaultCharset()}
@@ -314,10 +393,15 @@ public final BufferedReader errorReader() {
314393
* then the {@code InputStreamReader} will be reading from a
315394
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
316395
*
396+
* <p>The error reader should be {@linkplain BufferedReader#close closed}
397+
* when it is no longer needed.
398+
*
317399
* @apiNote
318-
* Using both {@link #getErrorStream} and {@link #errorReader(Charset)} has
319-
* unpredictable behavior since the buffered reader reads ahead from the
320-
* error stream.
400+
* Use either this method or the {@linkplain #getErrorStream error stream}
401+
* but not both on the same {@code Process}.
402+
* The error reader consumes and buffers bytes from the error stream.
403+
* Bytes read from the error stream would not be seen by the reader and the
404+
* buffer contents are unpredictable.
321405
*
322406
* <p>When the process has terminated, and the standard error has not been redirected,
323407
* reading of the bytes available from the underlying stream is on a best effort basis and
@@ -346,14 +430,17 @@ public final BufferedReader errorReader(Charset charset) {
346430
/**
347431
* Returns a {@code BufferedWriter} connected to the normal input of the process
348432
* using the native encoding.
349-
* Writes text to a character-output stream, buffering characters so as to provide
433+
* Writes text to a character-output stream, buffering characters to provide
350434
* for the efficient writing of single characters, arrays, and strings.
351435
*
352436
* <p>This method delegates to {@link #outputWriter(Charset)} using the
353437
* {@link Charset} named by the {@code native.encoding} system property.
354438
* If the {@code native.encoding} is not a valid charset name or not supported
355439
* the {@link Charset#defaultCharset()} is used.
356440
*
441+
* <p>The output writer should be {@linkplain BufferedWriter#close closed}
442+
* when it is no longer needed.
443+
*
357444
* @return a {@code BufferedWriter} to the standard input of the process using the charset
358445
* for the {@code native.encoding} system property
359446
* @since 17
@@ -365,7 +452,7 @@ public final BufferedWriter outputWriter() {
365452
/**
366453
* Returns a {@code BufferedWriter} connected to the normal input of the process
367454
* using a Charset.
368-
* Writes text to a character-output stream, buffering characters so as to provide
455+
* Writes text to a character-output stream, buffering characters to provide
369456
* for the efficient writing of single characters, arrays, and strings.
370457
*
371458
* <p>Characters written by the writer are encoded to bytes using {@link OutputStreamWriter}
@@ -383,6 +470,9 @@ public final BufferedWriter outputWriter() {
383470
* ProcessBuilder.redirectInput} then the {@code OutputStreamWriter} writes to a
384471
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
385472
*
473+
* <p>The output writer should be {@linkplain BufferedWriter#close closed}
474+
* when it is no longer needed.
475+
*
386476
* @apiNote
387477
* A {@linkplain BufferedWriter} writes characters, arrays of characters, and strings.
388478
* Wrapping the {@link BufferedWriter} with a {@link PrintWriter} provides
@@ -674,11 +764,12 @@ public long pid() {
674764
* free the current thread and block only if and when the value is needed.
675765
* <br>
676766
* For example, launching a process to compare two files and get a boolean if they are identical:
677-
* <pre> {@code Process p = new ProcessBuilder("cmp", "f1", "f2").start();
678-
* Future<Boolean> identical = p.onExit().thenApply(p1 -> p1.exitValue() == 0);
679-
* ...
680-
* if (identical.get()) { ... }
681-
* }</pre>
767+
* {@snippet lang = "java" :
768+
* Process p = new ProcessBuilder("cmp", "f1", "f2").start();
769+
* Future<Boolean> identical = p.onExit().thenApply(p1 -> p1.exitValue() == 0);
770+
* ...
771+
* if (identical.get()) { ... }
772+
* }
682773
*
683774
* @implSpec
684775
* This implementation executes {@link #waitFor()} in a separate thread
@@ -695,11 +786,11 @@ public long pid() {
695786
* External implementations should override this method and provide
696787
* a more efficient implementation. For example, to delegate to the underlying
697788
* process, it can do the following:
698-
* <pre>{@code
789+
* {@snippet lang = "java" :
699790
* public CompletableFuture<Process> onExit() {
700791
* return delegate.onExit().thenApply(p -> this);
701792
* }
702-
* }</pre>
793+
* }
703794
* @apiNote
704795
* The process may be observed to have terminated with {@link #isAlive}
705796
* before the ComputableFuture is completed and dependent actions are invoked.

0 commit comments

Comments
 (0)