When used with the `--system` option pointing to a different modularized JDK, `JavacTask` keeps `lib/jrt-fs.jar` and `lib/modules` open even after it is done. This is particularly problematic on Windows, where this makes it (temporarily) impossible to delete the files.
This is a problem for long-lived worker processes that respond to multiple compilation requests and receive the inputs, including the system JDK, in per-request directories that are cleaned up after compilation. This use case arises in the context of the build system Bazel.
For `jrt-fs.jar`, the reason is that the `URLClassLoader` that loads the `JrtFileSystemProvider` from it is never closed. This can be fixed in the host JDK by closing the class loader when the corresponding `JrtFileSystem` is closed.
For `modules`, the situation is more difficult: it is kept open as a result of a `FileChannel#map` call in `BasicImageReader`. Closing it explicitly appears to require `Unsafe#invokeCleaner` as well as reflection when done from the host JDK (seeJDK-4724038).
Run the following program on Linux or macOS with the path to another JDK as its only argument to reproduce the issue:
```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.List;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;class Scratch {
public static void main(String[] args) throws IOException, InterruptedException {
var compiler = ToolProvider.getSystemJavaCompiler();
var compilationUnit =
new SimpleJavaFileObject(URI.create("string:///Test.java"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return """
public class Test {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
""";
}
};
try (var fileManager = compiler.getStandardFileManager(null, null, null)) {
compiler
.getTask(
null, fileManager, null, List.of("--system", args[0]), null, List.of(compilationUnit))
.call();
} var process =
new ProcessBuilder()
.command("lsof", "-p", String.valueOf(ProcessHandle.current().pid()))
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start();
try (var stdout = process.getInputStream();
var reader = new BufferedReader(new InputStreamReader(stdout))) {
reader.lines().filter(line -> line.contains(args[0])).forEach(System.out::println);
}
process.waitFor();
}
}
```
This is a problem for long-lived worker processes that respond to multiple compilation requests and receive the inputs, including the system JDK, in per-request directories that are cleaned up after compilation. This use case arises in the context of the build system Bazel.
For `jrt-fs.jar`, the reason is that the `URLClassLoader` that loads the `JrtFileSystemProvider` from it is never closed. This can be fixed in the host JDK by closing the class loader when the corresponding `JrtFileSystem` is closed.
For `modules`, the situation is more difficult: it is kept open as a result of a `FileChannel#map` call in `BasicImageReader`. Closing it explicitly appears to require `Unsafe#invokeCleaner` as well as reflection when done from the host JDK (see
Run the following program on Linux or macOS with the path to another JDK as its only argument to reproduce the issue:
```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.List;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;class Scratch {
public static void main(String[] args) throws IOException, InterruptedException {
var compiler = ToolProvider.getSystemJavaCompiler();
var compilationUnit =
new SimpleJavaFileObject(URI.create("string:///Test.java"), JavaFileObject.Kind.SOURCE) {
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return """
public class Test {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
""";
}
};
try (var fileManager = compiler.getStandardFileManager(null, null, null)) {
compiler
.getTask(
null, fileManager, null, List.of("--system", args[0]), null, List.of(compilationUnit))
.call();
} var process =
new ProcessBuilder()
.command("lsof", "-p", String.valueOf(ProcessHandle.current().pid()))
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start();
try (var stdout = process.getInputStream();
var reader = new BufferedReader(new InputStreamReader(stdout))) {
reader.lines().filter(line -> line.contains(args[0])).forEach(System.out::println);
}
process.waitFor();
}
}
```