Claes Redestad
Java SE Performance Team, Oracle
The following is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.
JEP-282: "Link time is an opportunity to do whole-world optimizations that are otherwise difficult at compile time or costly at runtime."
$JAVA_HOME/bin/jlink --modulepath $JAVA_HOME/jmods --addmods java.se --output image
jlink --list-plugins
jlink ... --strip-debug --compress=2 --include-locales=en --class-for-name
[1] Latest API breaking change: 2016-07-28
Optimization: Replace with static references when valid:
jlink --class-for-name
public final class ClassForNamePlugin implements Plugin {
public ResourcePool transform(ResourcePool in,
ResourcePoolBuilder out) {
in.entries()
.forEach(resource -> out.add(transform(resource)));
return out.build();
}
ResourcePoolEntry transform(ResourcePoolEntry resource) {
/* use ASM to rewrite classes matching the pattern */
}
... some API changes to be expected
Observation: Significant time spent parsing and validating module-info.class classes for each module in the system image itself
Solution: Implement a jlink plugin to generate a pre-validated representation of the system module graph, jdk.internal.module.SystemModules
Also, since this is being done ahead-of-time we can profitably do various global optimizations, such as de-duplicating identical sets of export targets, that would be contraproductive if done at runtime
time java -version
Reduces memory use by a sizable amount, too!
(Actually, this plugin is always on, since nothing good would come from disabling it)
Observation: First usage of java.lang.invoke has a substantial cost: 100+ classes loaded, many of which are generated dynamically.
Solutions?
Most BMHs are generated in runtime; LFs are currently compiled to bytecode when initialized
JDK-8152641 introduce a plugin to generate BoundMethodHandles ahead of time:
jlink --generate-jli-classes=bmh::bmh-species=LL,L3,...
DMH pre-generation prototyped:
jlink --generate-jli-classes=bmh::bmh-species=LL,L3\ :invokeStatic=_V,L_V,LL_V\ :invokeSpecial=_V,L_V,LL_V\ :invokeVirtual=_V,L_V
All DMHs are generated into a single class and looked up speculatively, while dynamically generated DMHs are generated into an individual class.
One size does not fit all: default strategy likely needs to be conservatively tuned; specific deployments could choose to generate all DMHs needed into their custom runtime image
--generate-jli-classes
is cumbersome, and requires intimate knowledge of java.lang.invoke internals
While there are things jlink probably shouldn't do by default, there are many opportunities to do speculative, intrusive or application specific alterations
--order-resources
: arrange layout of resources in the runtime image based on a list of classes retrieved via profiling etc. Improves cold startup.
--include-locales
: Selectively include the locales you need to create a smaller image
--strip-debug
: Strip debug symbols to create a smaller image