ProcessHandle 让 Java 能观察和管理任意进程,不只是自己启动的子进程。这篇用真实的进程树量出它能读到什么、怎么遍历、通知有多快,以及 destroy 与 destroyForcibly 的真实含义。
pid = 26574,isAlive = true,supportsNormalTermination = true info.command = …/java info.commandLine = /opt/homebrew/opt/openjdk@21/bin/java -cp out Main info info.arguments = [-cp, out, Main, info] info.startInstant 有值 = true,info.totalCpuDuration = 31 ms,info.user = w parent = pid 26561 命令 Python 参数 exp/j262.py 对当前进程 destroy():IllegalStateException:destroy of current process not allowed allProcesses().count() = 742,用时 1 ms 对其中每个进程调用 info():有 command 的 454 / 742 个,用时 21 ms(没有权限的进程 command 为空)
父 shell:pid 26576 命令 bash 参数 -c sleep 61 & sleep 62 & sh -c 'sleep 63 & wait' & wait children() = [pid 26579 命令 bash 参数 -c sleep 63 & wait, pid 26578 命令 sleep 参数 62, pid 26577 命令 sleep 参数 61] descendants() 共 4 个:[pid 26579 bash -c sleep 63 & wait, pid 26578 sleep 62, pid 26577 sleep 61, pid 26580 sleep 63] destroyForcibly 只杀父 shell 之后:父 isAlive = false;原来的 4 个后代里仍然存活 4 个;它们现在的 parent = [pid 1, pid 26579] 逐个 destroyForcibly 之后仍然存活 0 个
子进程 sleep 1:onExit() 在 993 ms 时完成,isAlive = false,Process.exitValue = 0 非子进程(由已退出的 shell 派生,pid 26587):isAlive = true,parent = pid 1 它的 onExit() 完成:从调用起 1019 ms(sleep 1 剩余约 1000 ms 减去派生后经过的时间) 10 次 sleep 0.3 的 onExit 延迟(毫秒,排序):[303, 304, 304, 307, 308, 310, 311, 313, 313, 313],中位数 310 进程已退出后 ProcessHandle.of(pid) = Optional.empty
忽略 SIGTERM 的进程:destroy() 返回 true(只表示请求已发出),1.5 s 后 isAlive = true destroyForcibly() 返回 true,waitFor 返回退出码 137(= 128 + 信号 9),用时 1 ms,isAlive = false 普通 sleep 30 被 destroy():退出码 143(= 128 + 15,SIGTERM) 对已退出的进程再次 destroy():false,destroyForcibly():false 对 pid 1(launchd)destroy():false(没有权限) 清理检查:实验结束后 pgrep 还能找到的 sleep 61/62/63 进程 0 个