RFR: 8311302: Allow for jlinking a custom runtime without packaged modules being present
Severin Gehwolf
sgehwolf at openjdk.org
Fri Jul 7 08:40:47 UTC 2023
Please review this patch which adds a "jmodless" jlink mode to the JDK. Fundamentally this patch adds an option to use `jlink` even though your JDK install might not come with the packaged modules (directory `jmods`). This is particularly useful to further reduce the size of a jlinked runtime. After the removal of the concept of a JRE, a common distribution mechanism is still the full JDK with all modules and packaged modules. However, packaged modules can incur an additional size tax. For example in a container scenario it could be useful to have a base JDK container including all modules, but without also delivering the packaged modules. This comes at a size advantage of `~25%`. Such a base JDK container could then be used to `jlink` application specific runtimes, further reducing the size of the application runtime image (App + JDK runtime; as a single image *or* separate bundles, depending on the app being modularized).
The basic design of this approach is to add a jlink plugin for tracking non-class and non-resource files of a JDK install. I.e. files which aren't present in the jimage (`lib/modules`). This enables producing a `JmodLessArchive` class which has all the info of what constitutes the final jlinked runtime.
Basic usage example:
$ diff -u <(./bin/java --list-modules --limit-modules java.se) <(../linux-x86_64-server-release/images/jdk/bin/java --list-modules --limit-modules java.se)
$ diff -u <(./bin/java --list-modules --limit-modules java.se) <(../linux-x86_64-server-release/images/jdk/bin/java --list-modules --limit-modules jdk.jlink)
$ ls ../linux-x86_64-server-release/images/jdk/jmods
java.base.jmod java.net.http.jmod java.sql.rowset.jmod jdk.crypto.ec.jmod jdk.internal.opt.jmod jdk.jdi.jmod jdk.management.agent.jmod jdk.security.auth.jmod
java.compiler.jmod java.prefs.jmod java.transaction.xa.jmod jdk.dynalink.jmod jdk.internal.vm.ci.jmod jdk.jdwp.agent.jmod jdk.management.jfr.jmod jdk.security.jgss.jmod
java.datatransfer.jmod java.rmi.jmod java.xml.crypto.jmod jdk.editpad.jmod jdk.internal.vm.compiler.jmod jdk.jfr.jmod jdk.management.jmod jdk.unsupported.desktop.jmod
java.desktop.jmod java.scripting.jmod java.xml.jmod jdk.hotspot.agent.jmod jdk.internal.vm.compiler.management.jmod jdk.jlink.jmod jdk.naming.dns.jmod jdk.unsupported.jmod
java.instrument.jmod java.security.jgss.jmod jdk.accessibility.jmod jdk.httpserver.jmod jdk.jartool.jmod jdk.jpackage.jmod jdk.naming.rmi.jmod jdk.xml.dom.jmod
java.logging.jmod java.security.sasl.jmod jdk.attach.jmod jdk.incubator.vector.jmod jdk.javadoc.jmod jdk.jshell.jmod jdk.net.jmod jdk.zipfs.jmod
java.management.jmod java.se.jmod jdk.charsets.jmod jdk.internal.ed.jmod jdk.jcmd.jmod jdk.jsobject.jmod jdk.nio.mapmode.jmod
java.management.rmi.jmod java.smartcardio.jmod jdk.compiler.jmod jdk.internal.jvmstat.jmod jdk.jconsole.jmod jdk.jstatd.jmod jdk.random.jmod
java.naming.jmod java.sql.jmod jdk.crypto.cryptoki.jmod jdk.internal.le.jmod jdk.jdeps.jmod jdk.localedata.jmod jdk.sctp.jmod
$ ls jmods
ls: cannot access 'jmods': No such file or directory
$ ./bin/jlink --version
22-internal
$ ./bin/jlink --add-modules java.se --output ../build/java.se-runtime --verbose
java.base jrt:/java.base (jmod-less)
java.compiler jrt:/java.compiler (jmod-less)
java.datatransfer jrt:/java.datatransfer (jmod-less)
java.desktop jrt:/java.desktop (jmod-less)
java.instrument jrt:/java.instrument (jmod-less)
java.logging jrt:/java.logging (jmod-less)
java.management jrt:/java.management (jmod-less)
java.management.rmi jrt:/java.management.rmi (jmod-less)
java.naming jrt:/java.naming (jmod-less)
java.net.http jrt:/java.net.http (jmod-less)
java.prefs jrt:/java.prefs (jmod-less)
java.rmi jrt:/java.rmi (jmod-less)
java.scripting jrt:/java.scripting (jmod-less)
java.se jrt:/java.se (jmod-less)
java.security.jgss jrt:/java.security.jgss (jmod-less)
java.security.sasl jrt:/java.security.sasl (jmod-less)
java.sql jrt:/java.sql (jmod-less)
java.sql.rowset jrt:/java.sql.rowset (jmod-less)
java.transaction.xa jrt:/java.transaction.xa (jmod-less)
java.xml jrt:/java.xml (jmod-less)
java.xml.crypto jrt:/java.xml.crypto (jmod-less)
Providers:
java.desktop provides java.net.ContentHandlerFactory used by java.base
java.base provides java.nio.file.spi.FileSystemProvider used by java.base
java.naming provides java.security.Provider used by java.base
java.security.jgss provides java.security.Provider used by java.base
java.security.sasl provides java.security.Provider used by java.base
java.xml.crypto provides java.security.Provider used by java.base
java.base provides java.util.random.RandomGenerator used by java.base
java.management.rmi provides javax.management.remote.JMXConnectorProvider used by java.management
java.management.rmi provides javax.management.remote.JMXConnectorServerProvider used by java.management
java.desktop provides javax.print.PrintServiceLookup used by java.desktop
java.desktop provides javax.print.StreamPrintServiceFactory used by java.desktop
java.management provides javax.security.auth.spi.LoginModule used by java.base
java.desktop provides javax.sound.midi.spi.MidiDeviceProvider used by java.desktop
java.desktop provides javax.sound.midi.spi.MidiFileReader used by java.desktop
java.desktop provides javax.sound.midi.spi.MidiFileWriter used by java.desktop
java.desktop provides javax.sound.midi.spi.SoundbankReader used by java.desktop
java.desktop provides javax.sound.sampled.spi.AudioFileReader used by java.desktop
java.desktop provides javax.sound.sampled.spi.AudioFileWriter used by java.desktop
java.desktop provides javax.sound.sampled.spi.FormatConversionProvider used by java.desktop
java.desktop provides javax.sound.sampled.spi.MixerProvider used by java.desktop
java.logging provides jdk.internal.logger.DefaultLoggerFinder used by java.base
java.desktop provides sun.datatransfer.DesktopDatatransferService used by java.datatransfer
One nice property of this patch is that it can produce an identical (as in binary identical) `java.se` JDK image in "jmod-less" mode as compared to a regular jlink with packaged modules present. This has been asserted in newly added tests. In order to prevent accidental copy of modified files in the base JDK a checksum mechanism is in place to warn about this when jlinking. This is also asserted in tests.
One limitation of this mode is that there is no way to use it for cross-linking (at least currently). That is, a jmod-less `jlink` needs to happen on the same runtime platform the resulting image is intended to get deployed on.
Testing:
- [x] GHA (including `jdk/tools/jlink` tests). See for example [the latest run](https://github.com/jerboaa/jdk/actions/runs/5477743443) and [here](https://github.com/jerboaa/jdk/actions/runs/5475484765).
- [x] Some tests on our internal infra (JDK 17-based). It didn't show any regressions.
- [x] Added tests as part of the patch (8 of them). All pass on the major platforms.
Thoughts?
-------------
Commit messages:
- 8311302: Allow for jlinking a custom runtime without packaged modules being present
Changes: https://git.openjdk.org/jdk/pull/14787/files
Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=14787&range=00
Issue: https://bugs.openjdk.org/browse/JDK-8311302
Stats: 2083 lines in 23 files changed: 2016 ins; 33 del; 34 mod
Patch: https://git.openjdk.org/jdk/pull/14787.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/14787/head:pull/14787
PR: https://git.openjdk.org/jdk/pull/14787
More information about the core-libs-dev
mailing list