8357249: Compiler task keeps --system files open#2374
Conversation
|
👋 Welcome back dbeaumont! A progress list of the required criteria for merging this PR into |
|
❗ This change is not yet ready to be integrated. |
|
@david-beaumont this pull request can not be integrated into git checkout JDK_8357249
git fetch https://git.openjdk.org/valhalla.git lworld
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge lworld"
git push |
david-beaumont
left a comment
There was a problem hiding this comment.
Discussion points and notes added.
| isSystemProperty("sun.arch.data.model", "64", "32"); | ||
| private static final boolean USE_JVM_MAP = | ||
| isSystemProperty("jdk.image.use.jvm.map", "true", "true"); | ||
| // Whether to map the entire file contents. |
There was a problem hiding this comment.
It took me ages to realise this isn't "map all image files that are opened", but rather "map an entire image file if it is being mapped at all"
| private final FileChannel channel; | ||
| private final ImageHeader header; | ||
| private final long indexSize; | ||
| private final int indexSize; |
There was a problem hiding this comment.
Just in passing since it's not a long from the getter.
| this.name = this.imagePath.toString(); | ||
|
|
||
| // Only the runtime image is loaded with the root class-loader. | ||
| final boolean isRuntimeImage = BasicImageReader.class.getClassLoader() == null; |
There was a problem hiding this comment.
Semantic readability/intent.
| if (isRuntimeImage) { | ||
| map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); | ||
| } else { | ||
| // Non-runtime images are used for compiler tasks, and we avoid |
There was a problem hiding this comment.
This is what fixes the non-runtime jimage closing issue.
It's nasty because it goes from memory mapped file to 100M of copied data.
An alternative (with performance cost) would be to do only the "partial" loading of the buffer, say up to the index, and then fetch file content with normal file reads. But this is all open to much discussion.
There was a problem hiding this comment.
The changes look right but "Non-runtime images" is a not great phase. You could speak of the current run-time image vs. a target run-time image. That way the comments are clear - use memory-mapping for the current run-time image, regular file I/O for other run-time images.
| if ((long) imageFileSize != channel.size()) { | ||
| throw new IOException("\"" + name + "\" too large"); | ||
| } | ||
| map = readDirectBuffer(channel, imageFileSize, name); |
There was a problem hiding this comment.
Pulled out a little helper since it's used in 3 places.
|
|
||
| private volatile boolean closed; | ||
| // Set to null when closed to allow GC of underlying buffers to unmap files. | ||
| private final AtomicReference<SharedImageReader> reader; |
There was a problem hiding this comment.
This class is the place where we've already got lifetime semantics.
By letting the reader be cleared, we can replace the closed flag and retain non-locking patterns of use.
The benefit is that now the underlying reader becomes GC-able when the ImageReader is closed.
While this may not be of benefit when using direct buffers, it does let any mapped buffers get GC-ed sooner, which is required for closing the underlying memory mapped file.
I think this change is reasonable in general and just improves the semantics of the way this class's lifetime is managed wrt GC.
| isOpen = false; | ||
| image.close(); | ||
| image = null; | ||
|
|
There was a problem hiding this comment.
This is the change that lets the JAR file be closed. I hate it!
However it shows that there's potential for passing a "closer" or some kind into the JrtFileSystem from the provider to close the underling JarLoader (which is the actual thing that needs closing).
More to look at here, but this is proof of concept at least.
| useCtProps = false; | ||
| } | ||
| if (useCtProps && JRTIndex.isAvailable()) { | ||
| JRTIndex index = null; |
There was a problem hiding this comment.
Removes need for JRTIndex.isAvailable() which opened the uncloseable image in order to do its test.
We can just try and open the thing we want to open instead.
This should be identical in behaviour to what's there now and is necessary for preventing the non-runtime image being held open forever.
| } | ||
| } | ||
|
|
||
| /** {@return whether the JRT file-system is available to create an index} */ |
There was a problem hiding this comment.
Removed just to prove it's not needed anywhere else.
There was a problem hiding this comment.
I'm pretty sure this dates back to before the jrt file system worked with exploded builds.
| } | ||
| } | ||
|
|
||
| /** {@return whether the JRT file-system is available to create an index} */ |
There was a problem hiding this comment.
I'm pretty sure this dates back to before the jrt file system worked with exploded builds.
| ClassLoader cl = provider.getClass().getClassLoader(); | ||
| if (cl instanceof URLClassLoader) { | ||
| ((URLClassLoader) cl).close(); | ||
| } |
There was a problem hiding this comment.
JrtFileSystemProvider creates the JrtFsLoader/URLClassLoader and that might be a better place to close it. A postClose or some other callback to JrtFileSystemProvider could do this. It's just a suggestion to encapsulate everything about the custom and closeable class loader in one place.
There was a problem hiding this comment.
I tried looking at this, but there's no nice way to pass the classloader, or some delegate callback across the boundary (since you can't easily add a new API because you might be talking to old versions of the code).
Unless you're will to pass the class-loader in the env map, but that's pretty nasty.
I do really want to neaten this up, but might not get time.
There was a problem hiding this comment.
Okay, I was thinking something simple like provider.afterClose(this) so it's local to the jrtfs implementation (so version mismatch issues). We can look at it another time.
| if (isRuntimeImage) { | ||
| map = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); | ||
| } else { | ||
| // Non-runtime images are used for compiler tasks, and we avoid |
There was a problem hiding this comment.
The changes look right but "Non-runtime images" is a not great phase. You could speak of the current run-time image vs. a target run-time image. That way the comments are clear - use memory-mapping for the current run-time image, regular file I/O for other run-time images.
| } | ||
| map.rewind(); | ||
| return map; | ||
| } |
There was a problem hiding this comment.
I assume this doesn't really need to be a direct buffer, it will work equally well with a heap buffer.
In terms of naming, "readDirectBuffer" doesn't "read a direct buffer". It can be just "read". Same thing for the pre-existing "readBuffer".
As regards the != size check. A short read is possible, meaning it would be valid for read to return a value < size.
AlanBateman
left a comment
There was a problem hiding this comment.
Are you going to resolve the merge conflict and make the PR ready for review?
| /** | ||
| * Reads the image file to return a newly allocated buffer. | ||
| */ | ||
| private ByteBuffer copyBuffer(int size) |
There was a problem hiding this comment.
This reads "size" bytes from the start of the file into a buffer, and returns it. So something like readNBytes is a more appropriate name.
|
I'm not necessarily happy with this as it stands. It's more proof of concept. |
Draft request for discussion purposes only...
Progress
Issue
Reviewing
Using
gitCheckout this PR locally:
$ git fetch https://git.openjdk.org/valhalla.git pull/2374/head:pull/2374$ git checkout pull/2374Update a local copy of the PR:
$ git checkout pull/2374$ git pull https://git.openjdk.org/valhalla.git pull/2374/headUsing Skara CLI tools
Checkout this PR locally:
$ git pr checkout 2374View PR using the GUI difftool:
$ git pr show -t 2374Using diff file
Download this PR as a diff file:
https://git.openjdk.org/valhalla/pull/2374.diff