diff --git a/src/java.base/share/classes/java/lang/Process.java b/src/java.base/share/classes/java/lang/Process.java index 3e4837d2e0253..0164675082dc1 100644 --- a/src/java.base/share/classes/java/lang/Process.java +++ b/src/java.base/share/classes/java/lang/Process.java @@ -78,9 +78,42 @@ * process I/O can also be redirected * using methods of the {@link ProcessBuilder} class. * - *
The process is not killed when there are no more references to - * the {@code Process} object, but rather the process - * continues executing asynchronously. + *
{@linkplain ProcessBuilder#start() Starting a process} uses resources in both the invoking process and the invoked + * process and for the communication streams between them. + * The resources to control the process and for communication between the processes are retained + * until there are no longer any references to the Process or the input, error, and output streams + * or readers, or they have been closed. + * + *
The process is not killed when there are no more references to the {@code Process} object, + * but rather the process continues executing asynchronously. + * The process implementation closes file descriptors and handles for streams + * that are no longer referenced to prevent leaking operating system resources. + * Processes that have terminated or been terminated are monitored and their resources released. + * + *
Streams should be {@code closed} when they are no longer needed, to avoid delaying + * releasing the operating system resources. + * {@code Try-with-resources} can be used to open and close the streams. + *
For example, to capture the output of a program known to produce some output and then exit:
+ * {@snippet lang = "java" :
+ * List Stream resources (file descriptor or handle) are always paired; one in the invoking process
+ * and the other end of that connection in the invoked process.
+ * Closing a stream at either end terminates communication but does not have any direct effect
+ * on the other Process. Typically, the other process responds to the closing of the stream
+ * by exiting.
*
* There is no requirement that the process represented by a {@code
* Process} object execute asynchronously or concurrently with respect
@@ -127,6 +160,9 @@ public Process() {}
* then this method will return a
* null output stream.
*
+ * The output stream should be {@linkplain OutputStream#close closed}
+ * when it is no longer needed.
+ *
* @apiNote
* When writing to both {@link #getOutputStream()} and either {@link #outputWriter()}
* or {@link #outputWriter(Charset)}, {@link BufferedWriter#flush BufferedWriter.flush}
@@ -159,9 +195,14 @@ public Process() {}
* then the input stream returned by this method will receive the
* merged standard output and the standard error of the process.
*
+ * The input stream should be {@linkplain InputStream#close closed}
+ * when it is no longer needed.
+ *
* @apiNote
- * Use {@link #getInputStream()} and {@link #inputReader()} with extreme care.
- * The {@code BufferedReader} may have buffered input from the input stream.
+ * Avoid using both {@link #getInputStream} and {@link #inputReader(Charset)}.
+ * The input reader consumes and buffers bytes from the input stream.
+ * Bytes read from the input stream would not be seen by the reader and
+ * buffer contents are unpredictable.
*
* @implNote
* Implementation note: It is a good idea for the returned
@@ -185,9 +226,14 @@ public Process() {}
* then this method will return a
* null input stream.
*
+ * The error stream should be {@linkplain InputStream#close closed}
+ * when it is no longer needed.
+ *
* @apiNote
- * Use {@link #getErrorStream()} and {@link #errorReader()} with extreme care.
- * The {@code BufferedReader} may have buffered input from the error stream.
+ * Avoid using both {@link #getErrorStream} and {@link #inputReader(Charset)}.
+ * The error reader consumes and buffers bytes from the error stream.
+ * Bytes read from the error stream would not be seen by the reader and the
+ * buffer contents are unpredictable.
*
* @implNote
* Implementation note: It is a good idea for the returned
@@ -208,6 +254,9 @@ public Process() {}
* If the {@code native.encoding} is not a valid charset name or not supported
* the {@link Charset#defaultCharset()} is used.
*
+ * The reader should be {@linkplain BufferedReader#close closed}
+ * when it is no longer needed.
+ *
* @return a {@link BufferedReader BufferedReader} using the
* {@code native.encoding} if supported, otherwise, the
* {@link Charset#defaultCharset()}
@@ -238,6 +287,9 @@ public final BufferedReader inputReader() {
* then the {@code InputStreamReader} will be reading from a
* null input stream.
*
+ * The reader should be {@linkplain BufferedReader#close closed}
+ * when it is no longer needed.
+ *
* Otherwise, if the standard error of the process has been redirected using
* {@link ProcessBuilder#redirectErrorStream(boolean)
* ProcessBuilder.redirectErrorStream} then the input reader returned by
@@ -245,9 +297,10 @@ public final BufferedReader inputReader() {
* of the process.
*
* @apiNote
- * Using both {@link #getInputStream} and {@link #inputReader(Charset)} has
- * unpredictable behavior since the buffered reader reads ahead from the
- * input stream.
+ * Avoid using both {@link #getInputStream} and {@link #inputReader(Charset)}.
+ * The input reader consumes and buffers bytes from the input stream.
+ * Bytes read from the input stream would not be seen by the reader and the
+ * buffer contents are unpredictable.
*
* When the process has terminated, and the standard input has not been redirected,
* reading of the bytes available from the underlying stream is on a best effort basis and
@@ -283,6 +336,9 @@ public final BufferedReader inputReader(Charset charset) {
* If the {@code native.encoding} is not a valid charset name or not supported
* the {@link Charset#defaultCharset()} is used.
*
+ * The error reader should be {@linkplain BufferedReader#close closed}
+ * when it is no longer needed.
+ *
* @return a {@link BufferedReader BufferedReader} using the
* {@code native.encoding} if supported, otherwise, the
* {@link Charset#defaultCharset()}
@@ -314,10 +370,14 @@ public final BufferedReader errorReader() {
* then the {@code InputStreamReader} will be reading from a
* null input stream.
*
+ * The error reader should be {@linkplain BufferedReader#close closed}
+ * when it is no longer needed.
+ *
* @apiNote
- * Using both {@link #getErrorStream} and {@link #errorReader(Charset)} has
- * unpredictable behavior since the buffered reader reads ahead from the
- * error stream.
+ * Avoid using both {@link #getErrorStream} and {@link #errorReader(Charset)}.
+ * The error reader consumes and buffers bytes from the error stream.
+ * Bytes read from the error stream would not be seen by the reader and the
+ * buffer contents are unpredictable.
*
* When the process has terminated, and the standard error has not been redirected,
* reading of the bytes available from the underlying stream is on a best effort basis and
@@ -354,6 +414,9 @@ public final BufferedReader errorReader(Charset charset) {
* If the {@code native.encoding} is not a valid charset name or not supported
* the {@link Charset#defaultCharset()} is used.
*
+ * The output writer should be {@linkplain BufferedWriter#close closed}
+ * when it is no longer needed.
+ *
* @return a {@code BufferedWriter} to the standard input of the process using the charset
* for the {@code native.encoding} system property
* @since 17
@@ -383,6 +446,9 @@ public final BufferedWriter outputWriter() {
* ProcessBuilder.redirectInput} then the {@code OutputStreamWriter} writes to a
* null output stream.
*
+ * The output writer should be {@linkplain BufferedWriter#close closed}
+ * when it is no longer needed.
+ *
* @apiNote
* A {@linkplain BufferedWriter} writes characters, arrays of characters, and strings.
* Wrapping the {@link BufferedWriter} with a {@link PrintWriter} provides
@@ -674,11 +740,12 @@ public long pid() {
* free the current thread and block only if and when the value is needed.
* Starting a new process which uses the default working directory
* and environment is easy:
*
- * Here is an example that starts a process with a modified working
* directory and environment, and redirects standard output and error
* to be appended to a log file:
*
- * To start a process with an explicit set of environment
* variables, first call {@link java.util.Map#clear() Map.clear()}
@@ -506,10 +509,10 @@ public enum Type {
* This is the default handling of subprocess standard I/O.
*
* It will always be true that
- * It will always be true that
- * It will always be true that
- * It will always be true that
- * It will always be true that
- * It will always be true that
- * This is a convenience method. An invocation of the form
- *
* For example, launching a process to compare two files and get a boolean if they are identical:
- * {@code Process p = new ProcessBuilder("cmp", "f1", "f2").start();
- * Future
+ * {@snippet lang = "java" :
+ * Process p = new ProcessBuilder("cmp", "f1", "f2").start();
+ * Future{@code
+ * {@snippet lang = "java" :
* public CompletableFuture
+ * }
* @apiNote
* The process may be observed to have terminated with {@link #isAlive}
* before the ComputableFuture is completed and dependent actions are invoked.
diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java
index 9cb5848bdff55..6bc15893518d3 100644
--- a/src/java.base/share/classes/java/lang/ProcessBuilder.java
+++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java
@@ -150,30 +150,33 @@
* {@code
+ * {@snippet lang = "java" :
+
* Process p = new ProcessBuilder("myCommand", "myArg").start();
- * }
+ * }
+
*
* {@code
- * ProcessBuilder pb =
- * new ProcessBuilder("myCommand", "myArg1", "myArg2");
- * Map
+ * {@snippet lang = "java":
+ * ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
+ * Map {@code
- * Redirect.PIPE.file() == null &&
- * Redirect.PIPE.type() == Redirect.Type.PIPE
- * }
+ * {@snippet lang = "java" :
+ * Redirect.PIPE.file() == null &&
+ * Redirect.PIPE.type() == Redirect.Type.PIPE
+ * }
*/
public static final Redirect PIPE = new Redirect() {
public Type type() { return Type.PIPE; }
@@ -521,10 +524,10 @@ public enum Type {
* behavior of most operating system command interpreters (shells).
*
* {@code
- * Redirect.INHERIT.file() == null &&
- * Redirect.INHERIT.type() == Redirect.Type.INHERIT
- * }
+ * {@snippet lang = "java" :
+ * Redirect.INHERIT.file() == null &&
+ * Redirect.INHERIT.type() == Redirect.Type.INHERIT
+ * }
*/
public static final Redirect INHERIT = new Redirect() {
public Type type() { return Type.INHERIT; }
@@ -537,11 +540,10 @@ public enum Type {
* an operating system specific "null file".
*
* {@code
- * Redirect.DISCARD.file() is the filename appropriate for the operating system
- * and may be null &&
- * Redirect.DISCARD.type() == Redirect.Type.WRITE
- * }
+ * {@snippet lang = "java" :
+ * Redirect.DISCARD.file() // is the filename appropriate for the operating system
+ * Redirect.DISCARD.type() == Redirect.Type.WRITE
+ * }
* @since 9
*/
public static final Redirect DISCARD = new Redirect() {
@@ -572,10 +574,10 @@ boolean append() {
* Returns a redirect to read from the specified file.
*
* {@code
- * Redirect.from(file).file() == file &&
- * Redirect.from(file).type() == Redirect.Type.READ
- * }
+ * {@snippet lang = "java" :
+ * Redirect.from(file).file() == file &&
+ * Redirect.from(file).type() == Redirect.Type.READ
+ * }
*
* @param file The {@code File} for the {@code Redirect}.
* @return a redirect to read from the specified file
@@ -598,10 +600,10 @@ public String toString() {
* its previous contents will be discarded.
*
* {@code
- * Redirect.to(file).file() == file &&
- * Redirect.to(file).type() == Redirect.Type.WRITE
- * }
+ * {@snippet lang = "java" :
+ * Redirect.to(file).file() == file &&
+ * Redirect.to(file).type() == Redirect.Type.WRITE
+ * }
*
* @param file The {@code File} for the {@code Redirect}.
* @return a redirect to write to the specified file
@@ -628,10 +630,10 @@ public String toString() {
* system-dependent and therefore unspecified.
*
* {@code
- * Redirect.appendTo(file).file() == file &&
- * Redirect.appendTo(file).type() == Redirect.Type.APPEND
- * }
+ * {@snippet lang = "java" :
+ * Redirect.appendTo(file).file() == file &&
+ * Redirect.appendTo(file).type() == Redirect.Type.APPEND
+ * }
*
* @param file The {@code File} for the {@code Redirect}.
* @return a redirect to append to the specified file
@@ -914,15 +916,15 @@ public Redirect redirectError() {
* to be the same as those of the current Java process.
*
* {@code
- * pb.inheritIO()
- * }
+ * {@snippet lang = "java" :
+ * pb.inheritIO()
+ * }
* behaves in exactly the same way as the invocation
- * {@code
- * pb.redirectInput(Redirect.INHERIT)
- * .redirectOutput(Redirect.INHERIT)
- * .redirectError(Redirect.INHERIT)
- * }
+ * {@snippet lang = "java" :
+ * pb.redirectInput(Redirect.INHERIT)
+ * .redirectOutput(Redirect.INHERIT)
+ * .redirectError(Redirect.INHERIT)
+ * }
*
* This gives behavior equivalent to most operating system
* command interpreters, or the standard C library function
@@ -1176,22 +1178,21 @@ private Process start(Redirect[] redirects) throws IOException {
* @apiNote
* For example to count the unique imports for all the files in a file hierarchy
* on a Unix compatible platform:
- * {@code
- * String directory = "/home/duke/src";
- * ProcessBuilder[] builders = {
+ * {@snippet lang = "java" :
+ * String directory = "/home/duke/src";
+ * ProcessBuilder[] builders = {
* new ProcessBuilder("find", directory, "-type", "f"),
* new ProcessBuilder("xargs", "grep", "-h", "^import "),
* new ProcessBuilder("awk", "{print $2;}"),
* new ProcessBuilder("sort", "-u")};
- * List
*
* @param builders a List of ProcessBuilders
* @return a {@code List