JDK-8247536 : Support for pre-generated java.lang.invoke classes in CDS static archive
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2020-06-14
  • Updated: 2022-03-09
  • Resolved: 2020-10-10
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 16
16 b20Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Background
==========

In general, the java.lang.invoke framework would dynamically generate bytecodes according to the usage pattern of the current Java program (e.g., the actual types of parameters used in MethodHandle invocations). However, dynamic bytecode generation is expensive. For better start-up time, the framework tries to avoid dynamic generation and use pre-generated code instead. Examples are:

[1] ClassSpecializer::Factory::loadSpecies()
http://hg.openjdk.java.net/jdk/jdk/file/8ada048df69d/src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java

[2] InvokerBytecodeGenerator::lookupPregenerated()
http://hg.openjdk.java.net/jdk/jdk/file/8ada048df69d/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java

Since the run-time usage pattern is theoretically limitless, we cannot pre-generate all possible code. However, jlink uses profiling to look for commonly used patterns, pre-generates code for these patterns, and stores the pre-generated code into the JDK's lib/modules file. (See JDK-8086045).

http://hg.openjdk.java.net/jdk/jdk/file/8ada048df69d/make/GenerateLinkOptData.gmk

The pre-generated bytecodes for [1] are in these classes:

    $ jimage list lib/modules | grep Species_
    java/lang/invoke/BoundMethodHandle$Species_LL
    java/lang/invoke/BoundMethodHandle$Species_LJ
    java/lang/invoke/BoundMethodHandle$Species_LLL
    ...

For [2], the pre-generated bytecodes are in the methods of these classes

    java/lang/invoke/DirectMethodHandle$Holder
    java/lang/invoke/DelegatingMethodHandle$Holder
    java/lang/invoke/LambdaForm$Holder
    java.lang.invoke.Invokers$Holder

Problem
=======

The problem with the jlink pre-generated classes is that they may not match the usage pattern for all programs. E.g., a "fail" in the following log indicates that pregenerated code is not found and dynamic bytecodes must be generated:

$ javac -J-Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true HelloWorld.java | grep fail
[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeStatic L_L (fail)
[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeSpecial L3_V (fail)
[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeSpecial L4IIL_I (fail)
[LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder newInvokeSpecial L4II_L (fail)
...


Solution
========

When dumping the classlist of a Java program using -XX:DumpLoadedClassList, we also collect information about the java.lang.invoke usage pattern. During the creation of the static CDS archive (-Xshare:dump), we use the collected information to re-generate the various $Species and $Holder classes so that they are customized for this application.


Result
======

Preliminary testing shows that we can reduce the number of classes dynamically generated for java.lang.invoke by about 75% in the "javac HelloWorld.java" example, resulting in about 2.8% performance improvement.

Design Doc
=========

https://wiki.openjdk.java.net/display/HotSpot/Support+for+pre-generated+java.lang.invoke+classes+in+CDS+archive
Comments
The no-gen version when compiling HelloWorld: $J1/bin/java -Xlog:class+load -Xshare:on -XX:SharedArchiveFile=javac1.jsa HelloWorld.java | grep __JVM_LookupDefineClass__ [0.061s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801400400 source: __JVM_LookupDefineClass__ [0.118s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801408000 source: __JVM_LookupDefineClass__ [0.126s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801408400 source: __JVM_LookupDefineClass__ [0.127s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801408800 source: __JVM_LookupDefineClass__ [0.128s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801408c00 source: __JVM_LookupDefineClass__ [0.128s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140a000 source: __JVM_LookupDefineClass__ [0.132s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080140a400 source: __JVM_LookupDefineClass__ [0.135s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080140a800 source: __JVM_LookupDefineClass__ [0.136s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080140ac00 source: __JVM_LookupDefineClass__ [0.137s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140b000 source: __JVM_LookupDefineClass__ [0.138s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140b400 source: __JVM_LookupDefineClass__ [0.139s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140b800 source: __JVM_LookupDefineClass__ [0.140s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140bc00 source: __JVM_LookupDefineClass__ [0.141s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801410000 source: __JVM_LookupDefineClass__ [0.142s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801410400 source: __JVM_LookupDefineClass__ [0.157s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801410800 source: __JVM_LookupDefineClass__ [0.168s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801410c00 source: __JVM_LookupDefineClass__ [0.171s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801411000 source: __JVM_LookupDefineClass__ [0.172s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801411400 source: __JVM_LookupDefineClass__ [0.184s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801411800 source: __JVM_LookupDefineClass__ [0.186s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801411c00 source: __JVM_LookupDefineClass__ [0.196s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801418000 source: __JVM_LookupDefineClass__ [0.219s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801418400 source: __JVM_LookupDefineClass__ [0.221s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801418800 source: __JVM_LookupDefineClass__ [0.222s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801418c00 source: __JVM_LookupDefineClass__ [0.223s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801419000 source: __JVM_LookupDefineClass__ [0.244s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801419400 source: __JVM_LookupDefineClass__ [0.248s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801419800 source: __JVM_LookupDefineClass__ [0.294s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801419c00 source: __JVM_LookupDefineClass__ [0.296s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080141a000 source: __JVM_LookupDefineClass__ [0.300s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080141a400 source: __JVM_LookupDefineClass__ [0.301s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x000000080141a800 source: __JVM_LookupDefineClass__ Total 32 2) With regeneration of holder classes: $J2/bin/java -Xlog:class+load -Xshare:on -XX:SharedArchiveFile=javac2.jsa HelloWorld.java | grep __JVM_LookupDefineClass__ [0.069s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801400400 source: __JVM_LookupDefineClass__ [0.151s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801409000 source: __JVM_LookupDefineClass__ [0.152s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801409400 source: __JVM_LookupDefineClass__ [0.152s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801409800 source: __JVM_LookupDefineClass__ [0.153s][info][class,load] java.lang.invoke.LambdaForm$MH/0x0000000801409c00 source: __JVM_LookupDefineClass__ [0.155s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140a000 source: __JVM_LookupDefineClass__ [0.156s][info][class,load] java.lang.invoke.LambdaForm$MH/0x000000080140a400 source: __JVM_LookupDefineClass__ [0.302s][info][class,load] java.lang.invoke.LambdaForm$DMH/0x0000000801414000 source: __JVM_LookupDefineClass__ total 8 There still some classes not stored in CDS, a bug for this is https://bugs.openjdk.java.net/browse/JDK-8251851 The number of reduced class loading depends on code. This is for javac example.
23-04-2021

David, the numbers: inst inst intst-delta time time time-delta 1: 1895761709 1848906149 (-46855560) ---- 335.460 326.800 ( -8.660) ---- The final three lines are average of 10 loops (every loop run the command 40 times) The XX% are decrement of instructions and time elapse.
23-04-2021

Okay so what number should be in the XX% ?
23-04-2021

This is generated by using jdks, the one without regenerating the LF holder classes vs the one does: 1895761709,1848906149,335.460,326.800, 1898873690,1846628640,335.600,327.120, 1899470902,1851060904,336.170,326.050, 1895797143,1845495514,336.220,326.300, 1893523289,1848015626,336.330,327.270, 1898368962,1852030308,337.140,326.970, 1897720019,1848285715,337.990,326.750, 1898626990,1850096198,338.290,326.040, 1901555405,1845460341,335.870,329.040, 1894078814,1849458886,338.350,328.550, Results of " perf stat -r 40 bin/javac -J-Xshare:on -J-XX:SharedArchiveFile=javac2.jsa Bench_HelloWorld.java " 1: 1895761709 1848906149 (-46855560) ---- 335.460 326.800 ( -8.660) ---- 2: 1898873690 1846628640 (-52245050) ----- 335.600 327.120 ( -8.480) --- 3: 1899470902 1851060904 (-48409998) ---- 336.170 326.050 (-10.120) ---- 4: 1895797143 1845495514 (-50301629) ---- 336.220 326.300 ( -9.920) ---- 5: 1893523289 1848015626 (-45507663) ---- 336.330 327.270 ( -9.060) ---- 6: 1898368962 1852030308 (-46338654) ---- 337.140 326.970 (-10.170) ---- 7: 1897720019 1848285715 (-49434304) ---- 337.990 326.750 (-11.240) ----- 8: 1898626990 1850096198 (-48530792) ---- 338.290 326.040 (-12.250) ----- 9: 1901555405 1845460341 (-56095064) ----- 335.870 329.040 ( -6.830) --- 10: 1894078814 1849458886 (-44619928) ---- 338.350 328.550 ( -9.800) ---- ============================================================ 1897376174 1848542619 (-48833555) ---- 336.740 327.088 ( -9.653) ---- instr delta = -48833555 -2.5737% time delta = -9.653 ms -2.8665%
22-04-2021

The XX% in the Result section of the description needs to be replaced with a real number. :)
22-04-2021

Changeset: e4469d2c Author: Yumin Qi <minqi@openjdk.org> Date: 2020-10-10 02:06:52 +0000 URL: https://git.openjdk.java.net/jdk/commit/e4469d2c
10-10-2020

Here's an estimate of how "javac HelloWorld" can benefit from having more of its MethodHandle lambda forms pre-generated: http://cr.openjdk.java.net/~iklam/design/misc/8247536-cds-pregen-lambda-form--estimates/ Results of " perf stat -r 50 bin/javac -J-Xshare:on -J-XX:SharedArchiveFile=javac2.jsa Bench_HelloWorld.java " 1: 2239149872 2203293903 (-35855969) ---- 375.580 367.190 ( -8.390) ---- 2: 2245304165 2198900711 (-46403454) ----- 375.350 366.910 ( -8.440) ---- 3: 2242558895 2205167765 (-37391130) ---- 376.720 366.480 (-10.240) ----- 4: 2246851428 2198990987 (-47860441) ----- 375.119 367.010 ( -8.109) ---- 5: 2238549008 2202480450 (-36068558) ---- 374.100 367.600 ( -6.500) --- 6: 2240816859 2200725384 (-40091475) ---- 375.743 366.880 ( -8.863) ---- 7: 2243272464 2199639926 (-43632538) ----- 374.296 365.990 ( -8.306) ---- 8: 2239650882 2203211291 (-36439591) ---- 375.315 365.520 ( -9.795) ----- 9: 2242434935 2202380291 (-40054644) ---- 376.052 367.305 ( -8.747) ---- 10: 2238725993 2201342398 (-37383595) ---- 375.478 366.743 ( -8.735) ---- ============================================================ 2241729810 2201612439 (-40117371) ---- 375.375 366.762 ( -8.612) ---- instr delta = -40117371 -1.7896% time delta = -8.612 ms -2.2943%
14-06-2020