Introduction
Java 9 adds improvements to the Process API that deal with ongoing shortcommings like:
- getting the PID of the running process
- getting the children and/or the descendants of a process
Along with these a new class is added that helps:
- listing all running processes
- getting info for an arbitrary process
- traversing process tree
Note: The information returned by these methods is just a snapshot of the processes running on the OS. Some of them may be missing, stopped etc. during the traversal so handle with care.
Samples
Showing Process Information
Sample showing obtaining information for running processes’ id and command
1 2 3 4 5 |
public static void main(String[] args) { ProcessHandle.allProcesses().forEach(processHandle -> System.out.println("PID: " + processHandle.getPid() + " Command: " + processHandle.info().command().orElse("N/A"))); } |
Getting information for a process returns Optional
and based on that further filtering can be done. For example listing only processes that have available command property:
1 2 3 4 5 6 7 8 9 |
public static void main(String[] args) { Stream<ProcessHandle> processHandleStream = ProcessHandle.allProcesses().filter( processHandle -> processHandle.info().command().isPresent() ); processHandleStream.forEach(processHandle -> { System.out.println("PID: " + processHandle.getPid() + " Command: " + processHandle.info().command().orElse("N/A")); }); } |
You can see there is no ‘N/A’ value for ‘Command’ in the output
Sample showing obtaining information for an arbitrary process
1 2 3 4 5 6 7 8 |
public static void main(String[] args) { final int pid = 4784; ProcessHandle processHandle = ProcessHandle.of(pid).get(); System.out.println("PID: " + processHandle.getPid()); System.out.println("Command: " + processHandle.info().command().orElse("N/A")); System.out.println("CPU Duration: " + processHandle.info().totalCpuDuration().get().getSeconds() + " seconds"); } |
In this case the ‘pid’ is of the IDE used for writing the sample.
Getting the ID of the Running Process
1 2 3 4 5 6 |
public static void main(String[] args) { ProcessHandle processHandle = ProcessHandle.current(); System.out.println("PID: " + processHandle.getPid()); System.out.println("Command: " + processHandle.info().command().orElse("N/A")); System.out.println("CPU Duration: " + processHandle.info().totalCpuDuration().get().getSeconds() + " seconds"); } |
Sample showing obtaining information for a process that was killed after taking a snapshot from the OS
1 2 3 4 5 6 7 8 |
public static void main(String[] args) { final int pid = 4476; ProcessHandle processHandle = ProcessHandle.of(pid).get(); System.out.println("PID: " + processHandle.getPid()); System.out.println("Command: " + processHandle.info().command().orElse("N/A")); System.out.println("CPU Duration: " + processHandle.info().totalCpuDuration().get().getSeconds() + " seconds"); } |
The output of this snippet is:
1 2 3 4 5 |
PID: 4476 Command: N/A Exception in thread "main" java.util.NoSuchElementException: No value present at java.util.Optional.get(java.base@9-ea/Optional.java:138) at cd.sample.process.Main.main(Main.java:14) |
In this case the PID is of a text editor. To repro start the sample code in debug and once the processHandler is obtained stop the text editor.
Traversing process tree
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static void main(String[] args) throws IOException { final int pid = 1328; Runtime.getRuntime().exec("cmd"); System.out.println("Showing children processes"); ProcessHandle processHandle = ProcessHandle.of(pid).get(); processHandle.children().forEach(childProcess -> System.out.println("PID: " + childProcess.getPid() + " Command: " + childProcess.info().command().get()) ); System.out.println("Showing descendant processes"); processHandle.descendants().forEach(childProcess -> System.out.println("PID: " + childProcess.getPid() + " Command: " + childProcess.info().command().get()) ); } |
The output of this program is:
1 2 3 4 5 6 7 8 9 |
Showing children processes PID: 5488 Command: C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 2016.2.1\bin\fsnotifier.exe PID: 5000 Command: C:\Program Files\Java\jdk-9\bin\java.exe PID: 2864 Command: C:\Program Files\Java\jdk-9\bin\java.exe Showing descendant processes PID: 5488 Command: C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 2016.2.1\bin\fsnotifier.exe PID: 5000 Command: C:\Program Files\Java\jdk-9\bin\java.exe PID: 2864 Command: C:\Program Files\Java\jdk-9\bin\java.exe PID: 2748 Command: C:\Windows\System32\cmd.exe |
In this sample ‘pid’ is the running IDE. The output shows the difference between children()
and descendants()
. At the begging of the programm a new process is started however it is not listed when children()
is called and it is shown when descendants()
is called.
Sample Application
Sample application that shows:
- all process when invoked with argument ‘ps’
- process’ children when invoked with arguments ‘children <pid>’
- process’ descendants when invoked with arguments ‘descendants <pid>’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class App { public static void main(String[] args) throws IOException { final String command = args[0]; Stream<ProcessHandle> processStream = null; if ("ps".equals(command)) { processStream = ProcessHandle.allProcesses(); } else if ("children".equals(command)) { processStream = ProcessHandle.of(Long.valueOf(args[1])).get().children(); } else if ("descendants".equals(command)) { processStream = ProcessHandle.of(Long.valueOf(args[1])).get().descendants(); } if (processStream != null) { processStream.forEach(App::showInfo); } else { throw new IllegalArgumentException(); } } private static void showInfo(ProcessHandle processHandle) { System.out.println("PID: " + processHandle.getPid() + " Command: " + processHandle.info().command().orElse("N/A")); } } |
Source:App.java