Java 9 Process API:onExit()后如何获取非空ProcessHandle.Info?
我来帮你梳理下这个问题的原因和可行的解决思路~
首先,你遇到的Optional.empty情况,在Ubuntu 16.04这类Linux系统上其实是很常见的,核心原因和Linux的进程生命周期机制有关:
当子进程退出后,它会变成僵尸进程,直到父进程(这里就是你的JVM)回收它的退出状态和资源。在僵尸状态下,系统虽然还保留着进程的PID,但已经清理了进程的大部分详细信息(比如命令行、启动时间这些)。而Java的ProcessHandle.Info正是通过读取Linux的/proc/<pid>目录来获取进程信息的,一旦进程变成僵尸,这个目录就会被系统删除,自然就拿不到非空的Info了——这也是为什么你能拿到正确PID,但Info为空的原因。
接下来针对你的需求,分享几个可行的解决办法:
1. 在进程运行时提前获取并保存Info
这是最直接的方案:不要等到onExit()触发后再去获取Info,而是在进程启动后、还在运行的阶段就调用info(),此时/proc/<pid>目录还存在,能拿到完整的进程信息,你可以把这些信息先保存下来,后续即使进程退出也能使用。
示例代码:
Process process = new ProcessBuilder("your-command").start(); // 进程运行时获取Info,此时大概率是非空的 ProcessHandle.Info runningInfo = process.info() .orElseThrow(() -> new IllegalStateException("无法获取运行中进程的信息")); // 提前保存需要的信息 String command = runningInfo.command().orElse("未知命令"); Optional<Instant> startInstant = runningInfo.startInstant(); // 等待进程退出 process.onExit().thenAccept(ph -> { // 这里可以直接使用之前保存的runningInfo,而不是再调用ph.info() System.out.println("已退出进程的命令:" + command); startInstant.ifPresent(start -> System.out.println("启动时间:" + start)); });
2. 确保JVM及时回收子进程资源
有时候如果JVM没有及时处理子进程的退出状态,子进程会一直处于僵尸状态,导致Info无法获取。你可以尝试在onExit()的回调中主动调用Process.waitFor()或者ProcessHandle.destroy()(如果进程还没完全退出的话),不过这个方法对已经变成僵尸的进程效果有限,更适合作为预防手段。
3. 升级JDK或系统版本
Ubuntu 16.04的内核版本相对较旧,Java 9对Process API的实现可能在这个系统上存在一些兼容性局限。你可以尝试升级到Java 11及以上版本(后续JDK对Process API做了不少优化和修复),或者升级系统到更近期的Ubuntu版本,看看是否能解决退出后获取Info的问题。
需要注意的是:Linux系统本身的机制决定了,进程退出后如果没有提前保存信息,是无法再获取到完整的ProcessHandle.Info的,所以最可靠的方案还是在进程运行时提前获取并缓存信息。
内容的提问来源于stack exchange,提问作者Maciek Murawski

