| Owner | Mandy Chung |
| Type | Feature |
| Scope | JDK |
| Status | Closed / Delivered |
| Release | 18 |
| Component | core-libs / java.lang:reflect |
| Discussion | core dash libs dash dev at openjdk dot java dot net |
| Effort | M |
| Duration | M |
| Reviewed by | Alan Bateman, John Rose |
| Endorsed by | John Rose |
| Created | 2021/04/26 22:41 |
| Updated | 2024/03/07 18:49 |
| Issue | 8266010 |
Reimplementjava.lang.reflect.Method,Constructor, andField on top ofjava.lang.invoke method handles. Making method handles the underlyingmechanism for reflection will reduce the maintenance and development cost ofboth thejava.lang.reflect andjava.lang.invoke APIs.
It is not a goal to make any change to thejava.lang.reflect API. This issolely an implementation change.
Core reflection has two internal mechanisms for invoking methods andconstructors. For fast startup, it uses native methods in the HotSpot VM forthe first few invocations of a specific reflective method or constructor object.For better peak performance, after a number of invocations it generates bytecodefor the reflective operation and uses that in subsequent invocations.
For field access, core reflection uses the internalsun.misc.Unsafe API.
With thejava.lang.invoke method-handle API introduced in Java 7, thereare altogether three different internal mechanisms for reflective operations:
VM native methods,
Dynamically generated bytecode stubs forMethod::invoke andConstructor::newInstance, along withUnsafe field access forField::get andset, and
Method handles.
When we updatejava.lang.reflect andjava.lang.invoke to support newlanguage features, such as those envisioned inProject Valhalla, wemust modify all three code paths, which is costly. In addition, the currentimplementation relies on special treatment by the VM of the generated bytecode,which is wrapped in subclasses ofjdk.internal.reflect.MagicAccessorImpl:
Accessibility is relaxed so that these classes can access inaccessiblefields and methods of other classes,
Verification is disabled to work aroundJLS §6.6.2 in order tosupport reflection onObject::clone, and
A non-well-behaved class loader is used to work around some security andcompatibility issues.
Reimplementjava.lang.reflect on top of method handles as the commonunderlying reflective mechanism of the platform by replacing thebytecode-generating implementations ofMethod::invoke,Constructor::newInstance,Field::get, andField::set.
The new implementation performs direct invocations of the method handlesfor specific reflective objects.We use the VM's native reflection mechanism only during early VM startup,before the method-handle mechanism is initialized. That happens soon afterSystem::initPhase1 and beforeSystem::initPhase2, after which we switch to using methodhandles exclusively. This benefitsProject Loom by reducing the use ofnative stack frames.
For optimal performance,Method,Constructor, andField instancesshould be held instatic final fields so that they can beconstant-folded by the JIT. When that is done, microbenchmarks show thatthe performance of the new implementation is significantly faster thanthe old implementation, by 43–57%.
WhenMethod,Constructor, andField instances are held innon-constant fields (e.g., in a non-final field or an array element),microbenchmarks show some performance degradation. The performance offield accesses is significantly slower than the old implementation, by51–77%, whenField instances cannot be constant-folded.
This degradation may, however, not have much effect on the performance ofreal-world applications. We ran several serialization anddeserialization benchmarks using real-world libraries and found nodegradation in
We will continue to explore opportunities to improve performance, forexample by refining the bytecode shapes for field access to enableconcreteMethodHandles andVarHandles to be reliably optimized by theJIT regardless of whether the receiver is constant.
The new implementation will reduce the cost of upgrading reflectionsupport for new language features and, further, will allow us to simplifythe HotSpot VM by removing the special treatment ofMagicAccessorImplsubclasses.
Retain the existing core reflection implementation to avoid any compatibilityrisk. The dynamic bytecode generated for core reflection would remain at classfile version 49, and the VM would continue to treat such bytecode specially.
We reject this alternative because
The cost of updatingjava.lang.reflect andjava.lang.invoke to supportProject Valhalla's primitive classes and generic specialization would behigh,
Additional special rules in the VM would likely be needed to support newlanguage features within the limitation of the old class-file format, and
Project Loom would need to find a way to handle the introduction of nativestack frames by core reflection.
Replace the bytecode writer used by core reflection to use a new bytecodelibrary that evolves together with the class-file format, but otherwise retainthe existing core reflection implementation and continue to treatdynamically-generated reflection bytecode specially.
This alternative has lower compatibility risk than what we propose above, but itis still a sizeable amount of work and it still has the first and lastdisadvantages of the first alternative.
Comprehensive testing will ensure that the implementation is robust andcompatible with existing behavior. Performance testing will ensure that thereare no unexpected significant performance regressions compared to the currentimplementation. We will encourage developers using early-access builds to testas many libraries and frameworks as possible in order to help us identify anybehavior or performance regressions.
Benchmark Mode Cnt Score Error UnitsReflectionSpeedBenchmark.constructorConst avgt 10 68.049 ± 0.872 ns/opReflectionSpeedBenchmark.constructorPoly avgt 10 94.132 ± 1.805 ns/opReflectionSpeedBenchmark.constructorVar avgt 10 64.543 ± 0.799 ns/opReflectionSpeedBenchmark.instanceFieldConst avgt 10 35.361 ± 0.492 ns/opReflectionSpeedBenchmark.instanceFieldPoly avgt 10 67.089 ± 3.288 ns/opReflectionSpeedBenchmark.instanceFieldVar avgt 10 35.745 ± 0.554 ns/opReflectionSpeedBenchmark.instanceMethodConst avgt 10 77.925 ± 2.026 ns/opReflectionSpeedBenchmark.instanceMethodPoly avgt 10 96.094 ± 2.269 ns/opReflectionSpeedBenchmark.instanceMethodVar avgt 10 80.002 ± 4.267 ns/opReflectionSpeedBenchmark.staticFieldConst avgt 10 33.442 ± 2.659 ns/opReflectionSpeedBenchmark.staticFieldPoly avgt 10 51.918 ± 1.522 ns/opReflectionSpeedBenchmark.staticFieldVar avgt 10 33.967 ± 0.451 ns/opReflectionSpeedBenchmark.staticMethodConst avgt 10 75.380 ± 1.660 ns/opReflectionSpeedBenchmark.staticMethodPoly avgt 10 93.553 ± 1.037 ns/opReflectionSpeedBenchmark.staticMethodVar avgt 10 76.728 ± 1.614 ns/opBenchmark Mode Cnt Score Error UnitsReflectionSpeedBenchmark.constructorConst avgt 10 32.392 ± 0.473 ns/opReflectionSpeedBenchmark.constructorPoly avgt 10 113.947 ± 1.205 ns/opReflectionSpeedBenchmark.constructorVar avgt 10 76.885 ± 1.128 ns/opReflectionSpeedBenchmark.instanceFieldConst avgt 10 18.569 ± 0.161 ns/opReflectionSpeedBenchmark.instanceFieldPoly avgt 10 98.671 ± 2.015 ns/opReflectionSpeedBenchmark.instanceFieldVar avgt 10 54.193 ± 3.510 ns/opReflectionSpeedBenchmark.instanceMethodConst avgt 10 33.421 ± 0.406 ns/opReflectionSpeedBenchmark.instanceMethodPoly avgt 10 109.129 ± 1.959 ns/opReflectionSpeedBenchmark.instanceMethodVar avgt 10 90.420 ± 2.187 ns/opReflectionSpeedBenchmark.staticFieldConst avgt 10 19.080 ± 0.179 ns/opReflectionSpeedBenchmark.staticFieldPoly avgt 10 92.130 ± 2.729 ns/opReflectionSpeedBenchmark.staticFieldVar avgt 10 53.899 ± 1.051 ns/opReflectionSpeedBenchmark.staticMethodConst avgt 10 35.907 ± 0.456 ns/opReflectionSpeedBenchmark.staticMethodPoly avgt 10 102.895 ± 1.604 ns/opReflectionSpeedBenchmark.staticMethodVar avgt 10 82.123 ± 0.629 ns/opCode that depends upon highly implementation-specific and undocumented aspectsof the existing implementation may be impacted. To mitigate this compatibilityrisk, as a workaround you can enable the old implementation via-Djdk.reflect.useDirectMethodHandle=false.
Code that inspects the internal generated reflection classes (i.e.,subclasses ofMagicAccessorImpl) will no longer work and must be updated.
Method-handle invocation may consume more resources than the old corereflection implementation. Such invocation involves calling multiple Javamethods to ensure that the declaring class of a member is initialized prior toaccess, and thus may require more stack space for the necessary executionframes. This may result in aStackOverflowError or, if aStackOverflowErroris thrown while initializing a class, then aNoClassDefFoundError.
We will remove the old core reflection implementation in a future release.The-Djdk.reflect.useDirectMethodHandle=false workaround will stop working at that point.