Refactor launcher AppCDS logic#8708
Conversation
The AppCDS file is always written if --cache is specified or it's older than the JRuby JAR. If Java is 19 or newer, reading and writing are handled automatically by the JVM. Otherwise, the launcher decides whether to read or write.
db65466 to
a28af9a
Compare
|
Very nice! High priority to get this landed for the upcoming 10 release. I'll review. I chatted with @enebo as well and we agree that it would be good to include the Java version in the JSA filename, so every Java version gets its own file. Same would go for the log output when that is enabled. On my system, JRuby 10 + Java 21 JSA file is only 5.4MB so I don't think it's a big deal to leave a few lying around. We also chatted about where we should store these files. I think it's fine to go with JRuby's |
|
@mrnoname1000 You still have this marked as draft... is there more work coming? |
|
It's still a draft because I haven't implemented version in the JSA filename yet, but I'm home from work now so I can add that last feature, This PR should be working though if you want the existing work merged sooner. |
|
Go ahead and add that and then I'll review. |
|
I added the version string to the JSA filename as well as a flag to remove all the JSA files we may have generated. If we find out JSA files have compatibility guarantees between point releases, we can trim off parts of the version string to allow sharing between those releases. |
headius
left a comment
There was a problem hiding this comment.
There's some minor issues and some regressed behavior (--cache).
|
We have some command line tests under test/jruby that could be expanded for this new functionality. |
|
I'll retest locally with latest changes. 👍 |
|
So far everything is working well on JRuby 10! A wild question appears: should |
|
Issue with 9.4 under Java 11: I'm not sure this particular flag existed until after 11. Earlier CDS support required a couple flags and in some cases, multiple steps. I'm thinking maybe we don't include the automatic CDS feature in the launcher unless we're on a Java that supports one-shot "at exit" dumping. There's still the |
|
See here for the Java 11 equivalent, which does appear to require a two step process (dump class list, then generate CDS archive): https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-31503FCE-93D0-4175-9B4F-F6A738B2F4C4 This page documents the feature for Java 10, but it was still a "commercial" feature at that point: https://docs.oracle.com/javase/10/tools/java.htm#JSWOR-GUID-31503FCE-93D0-4175-9B4F-F6A738B2F4C4 I believe Java 13 is the first release to support the "one shot" And you appear to be right that 19 introduces the automatic flag: https://docs.oracle.com/en/java/javase/19/docs/specs/man/java.html#creating-dynamic-cds-archive-file-with--xxautocreatesharedarchive So I say we should disable the automatic dumping as well as the @mrnoname1000 I can make this change after merging, or you can make it if you want. I'm keen to get this landed so I can do some other version-specific flags (e.g. for #8696). |
Java 13 introduces the "at exit" flag. Prior to that, creating a CDS archive requires a two-step dance of generating a class list and then using that list to create the archive. To make this feature simpler and focus on the JDK levels that support it the way we want, we'll limit it to Java 13+.
|
@mrnoname1000 I made the change... let me know if it looks ok. |
|
Hmm, my change doesn't seem to be enough. It's still trying to pass the Java 13+ flag: |
|
Yeah definitely don't want to do that two step dance. The change you made should have bumped the required version up to 13, I can investigate in a few minutes here and I think we should add some version into to the debug logs. |
|
The AppCDS logic was linked with the modular Java logic, so any modular Java was coming up as wanting AppCDS enabled. I changed it to default to true/false depending on if it's supported, then allowing reassignment later. There is still some weirdness if --cache is passed and it isn't supported since
Not sure, logs probably won't be big but could clutter up the lib directory. Deleting logs would probably be in the spirit of the option as long as it's documented. |
How would we test the new functionality that depends on the current version of Java? |
I'm not sure yet. We will add tests to test/jruby/test_command_line_options.rb (or whatever it's called) and run those on all the supported levels of 9.4 and 10. That's probably enough. The other option is having the test actually use multiple Javas and I have no idea how to do that cleanly. |
|
A new unfortunate wrinkle: Eclipse Temurin builds of OpenJDK on macos on Apple Silicon do not include the base JDK CDS dump, so you get cryptic errors: adoptium/adoptium-support#937 Switching to a Zulu build works correctly with all versions I tried. I expressed my discontent on the Adoptium issue above... hopefully they will have an answer or we'll need to also detect a CDS file in the JDK before starting. 😡 |
|
On my Zulu 21 install the built-in jsa is here:
( I suppose we could just check if there's any .jsa file in the |
All we need to do is Edit: Missed the fact that they're always in |
|
This is what the patch would look like, I can push it if you want: diff --git a/bin/jruby.sh b/bin/jruby.sh
index 8cd0aec5e3..66e080bac0 100755
--- a/bin/jruby.sh
+++ b/bin/jruby.sh
@@ -145,6 +145,18 @@ a_isempty() {
return 0
}
+# exists [FILE...]
+#
+# Returns 0 if all FILEs exist or none provided, otherwise returns 1
+exists() {
+ while [ "$#" -gt 0 ]; do
+ [ -e "$1" ] || return
+ shift
+ done
+
+ return 0
+}
+
# is_newer FILE OTHER...
#
# Returns 0 if FILE is newer than all OTHER files. If FILE doesn't exist,
@@ -500,7 +512,7 @@ add_log "Detected Java version: $java_version"
java_major=${java_version%%.*}
# AppCDS support
-if [ "$java_major" -ge 13 ]; then
+if [ "$java_major" -ge 13 ] && exists "$JAVA_HOME"/lib/server/*.jsa; then
java_has_appcds=true
else
java_has_appcds=false |
|
Let's hold off and do this additional detection separately. I heard back from the Temurin folks and they say builds since January should include the missing files. Maybe this problem will just go away, but if it doesn't we can add the detection in. |
|
My worry is just that JRuby will suddenly break for anyone with these JDKs unless they pass the as-yet undocumented |
|
@mrnoname1000 That's a fair point. Let's go with the patch you have and we can refine it after we land this PR. |
|
Also, let's make |
|
An additional discussion or requirement: if we can't write to the JRuby lib dir, we shouldn't error... but should we warn? I'm not sure. |
9bc4477 to
c3b3ef4
Compare
|
Added the discussed features:
|
c3b3ef4 to
09ebce4
Compare
|
Actually the JSA file is created without write permissions for some reason so I changed it to just check the parent directory |
Yeah I think this is a holdover from the JDK-level archive that they don't want anyone to overwrite, but it's definitely a little weird. |
|
I've been running with this latest version since you pushed it and everything works great on Java 21 and 23 with JRuby master. I'm going to merge this and we can make additional tweaks there. Excellent work, @mrnoname1000, thank you! I see a few next steps:
I also still like the idea of making this a separate project (module, repository, whatever) that gets installed on build. If it's a gem, it would mean users can upgrade the launcher any time. But either way having it sourced from one place would mean we're always including the latest cool launcher script features. And of course we still need to explore what it might take to build a native executable with this and busybox, or else help @enebo finish his Rust launcher. |
The launcher now detects the Java version and uses the appropriate AppCDS flags. If I researched correctly, AppCDS is supported on Java 10+ and the AutoCreateSharedArchive flag on 19+. The modification time of the JRuby JAR is used to determine if the AppCDS archive should be explicitly regenerated.
The Java version currently does not factor into the regeneration calculation. The easiest solution would be to have an archive for every Java version, which would only involve sticking the relevant version in the filename, but could get unwieldy without a way to clean up old archives. We could also store the last-used version in a sidecar file, which also wouldn't be too bad since we already need access to the JRuby folder. I could use some input on what strategy we would prefer.