Java 9启动第三方程序后lib\modules文件锁定问题及解决方案问询
我太懂这个糟心的问题了——用Java 9启动个记事本之类的第三方程序,退出Java应用后居然发现JRE里的lib\modules文件被死死锁住,连带着整个JRE目录都没法重命名,给带私有JRE的应用更新添了大麻烦。这个确实是Java 9的已知Bug,对应的编号是JDK-8194734。
下面给你几个Windows下可行的变通方案,亲测有效的那种:
1. 用外部代理程序(首推)
写个极简的原生Windows小工具(用C#或C++都可以),它唯一的功能就是接收你要启动的应用路径,启动之后立刻退出。这样代理程序完全不会持有Java的lib\modules文件锁,Java应用启动完代理就能正常释放所有资源。
举个C#的例子,编译成exe就行:
using System.Diagnostics; namespace AppLauncher { class Program { static void Main(string[] args) { if (args.Length > 0) { // 启动目标应用,不等待它退出 Process.Start(args[0]); } } } }
然后把Java代码改成启动这个代理,把目标应用路径当参数传进去:
import java.io.IOException; public class LaunchNotepad { public static void main(String[] args) throws IOException { Runtime.getRuntime().exec(new String[] {"AppLauncher.exe", "C:\\Windows\\notepad.exe"}); } }
这样Java应用退出后,代理早就完成任务跑没影了,根本不会碰JRE的文件。
2. 调整ProcessBuilder的句柄继承设置(部分场景有效)
试试用ProcessBuilder替代Runtime.exec,显式关闭子进程对父进程文件句柄的继承:
import java.io.IOException; public class LaunchNotepad { public static void main(String[] args) throws IOException { ProcessBuilder pb = new ProcessBuilder("C:\\Windows\\notepad.exe"); pb.inheritIO(false); // 禁止子进程继承父进程的IO和文件句柄 pb.start(); } }
不过这个方法不是100%通用,因为Java 9的模块系统在句柄继承上有特殊逻辑,有些环境下可能还是会锁文件,所以优先级不如外部代理。
3. 延迟更新JRE目录(临时凑合用)
如果暂时没法搞外部代理,可以在Java应用退出后等个几秒再尝试更新JRE目录——但这只是权宜之计,因为锁的释放时间不确定,说不定哪天就掉链子。
为啥会出现这个问题?
本质是Java 9的模块系统在启动子进程时,不小心让子进程继承了lib\modules文件的打开句柄,导致Java进程退出后,子进程还攥着这个句柄不放,自然就锁死了文件。外部代理的核心就是切断这种继承关系,从根上解决问题。
内容的提问来源于stack exchange,提问作者Thomas S.

