Replace java.lang.reflect with MethodHandle/VarHandle across core modules#3300
Open
He-Pin wants to merge 4 commits into
Open
Replace java.lang.reflect with MethodHandle/VarHandle across core modules#3300He-Pin wants to merge 4 commits into
He-Pin wants to merge 4 commits into
Conversation
pjfanning
reviewed
Jul 6, 2026
pjfanning
reviewed
Jul 6, 2026
pjfanning
reviewed
Jul 6, 2026
pjfanning
reviewed
Jul 6, 2026
pjfanning
reviewed
Jul 6, 2026
pjfanning
reviewed
Jul 6, 2026
Member
Author
|
This pR is not ready, let's mark it to draft for now |
He-Pin
added a commit
that referenced
this pull request
Jul 6, 2026
Motivation: PR #3300 replaced reflective constructor access with MethodHandles, but public JDK constructors in non-open modules were forced through privateLookupIn and failed under Scala 3 CI during throwable deserialization. Modification: Prefer MethodHandles.publicLookup for public constructor handles and fall back to privateLookupIn only when needed. Use constructor metadata for no-arg constructor checks and add a regression test for public JDK constructors. Result: TimeoutException deserializes back to its original type and constructor lookup avoids unnecessary deep access on public constructors. Tests: - rtk bash -lc export JAVA_HOME=$(/usr/libexec/java_home -v 17); export PATH=$JAVA_HOME/bin:$PATH; sbt ++3.3.8 actor-tests/Test/testOnly org.apache.pekko.util.ReflectSpec - passed, 11 tests - rtk bash -lc export JAVA_HOME=$(/usr/libexec/java_home -v 17); export PATH=$JAVA_HOME/bin:$PATH; sbt ++3.3.8 remote/Test/testOnly org.apache.pekko.remote.serialization.MiscMessageSerializerSpec - passed, 103 tests - rtk bash -lc export JAVA_HOME=$(/usr/libexec/java_home -v 17); export PATH=$JAVA_HOME/bin:$PATH; sbt ++3.3.8 actor-tests/Test/testOnly org.apache.pekko.actor.ActorCreationTest - passed, 7 tests - rtk scalafmt --mode diff-ref=origin/main - passed - rtk scalafmt --list --mode diff-ref=origin/main - passed - rtk git diff --check - passed References: Refs #3300
…s core modules Motivation: java.lang.reflect Method.invoke, Constructor.newInstance, and Field.get/set bypass JIT inlining, adding overhead in critical paths like actor instantiation, protobuf serialization, and virtual thread management. Modification: - ReflectiveDynamicAccess: replace Constructor.newInstance with MethodHandle.invokeWithArguments; replace Field.get(null) for MODULE$ with VarHandle.get - Reflect: replace Constructor.newInstance with MethodHandle.invoke; use Class.forName universally; prefer publicLookup for public constructors - VirtualThreadSupport: replace Field.set/get with VarHandle.set/get - ProtobufSerializer: replace Method-based caches with MethodHandle caches - LineNumbers: replace Method.invoke with MethodHandle.invoke - ByteBufferCleaner: replace Field.get(null) with VarHandle.get - ExtensionsImpl: replace Method.invoke(null) with MethodHandle.invoke - Replace java.lang.reflect with ClassGraph in test suites Result: JIT-inlinable field access and method invocation across all core reflection utilities, improving performance in actor creation, serialization, and virtual thread operations. Tests: - sbt ++3.3.8 actor-tests/Test/testOnly org.apache.pekko.util.ReflectSpec - sbt ++3.3.8 remote/Test/testOnly org.apache.pekko.remote.serialization.MiscMessageSerializerSpec - sbt ++3.3.8 actor-tests/Test/testOnly org.apache.pekko.actor.ActorCreationTest Refs #3300
f3c07c6 to
aa26fbe
Compare
Motivation:\nAvoid invokeWithArguments() overhead on no-arg method-handle calls in core runtime reflection helpers.\n\nModification:\nUse MethodHandle.invoke() for static extension lookup, DNS resolver open(), and zero-arg constructor instantiation.\n\nResult:\nThe MethodHandle migration keeps its compatibility gains while trimming avoidable dispatch and allocation overhead.
pjfanning
reviewed
Jul 7, 2026
pjfanning
reviewed
Jul 7, 2026
pjfanning
reviewed
Jul 7, 2026
pjfanning
reviewed
Jul 7, 2026
pjfanning
reviewed
Jul 7, 2026
Motivation: Address review feedback for PR #3300 and avoid replacing safe class loading and metadata reflection. Modification: Restore ordinary Class.forName and method metadata reflection where no deep reflective access is involved. Remove the ClassGraph test dependency and related package scanning. Cache MethodHandles lookups and fixed MethodType instances on the remaining MethodHandle paths. Result: Deep reflection remains handled by MethodHandle or VarHandle APIs while ordinary class loading keeps the original behavior and lower overhead. Tests: - rtk scalafmt --mode diff-ref=origin/main - rtk scalafmt --list --mode diff-ref=origin/main - sbt javafmtAll with JDK 17 - sbt javafmtCheckAll with JDK 17 - rtk git diff --check - focused actor, stream, remote, DNS, persistence, persistence-tck, cluster-sharding-typed, and cluster-sharding MultiJvm sbt validation - JDK21 virtual thread focused sbt validation References: Refs #3300
Motivation: Code review of PR #3300 identified several MethodHandle/VarHandle instances that were re-created on every invocation, negating the performance benefit of migrating from reflection. Additionally, DnsSettings used publicLookup without fallback for --add-opens users. Modification: - VirtualThreadSupport: cache all MethodHandles/VarHandles as lazy vals - LineNumbers: cache writeReplace MethodHandle per lambda class via ConcurrentHashMap (matching Reflect.scala pattern) - DnsSettings: add privateLookupIn fallback when publicLookup fails on non-exported JDK-internal sun.net.dns package - Reflect: replace non-idiomatic return with if/else expression - BoxedType: remove unused primitiveForBoxed and toPrimitive Result: MethodHandle lookups execute once and are reused on subsequent calls. DnsSettings works with both --add-exports and --add-opens configurations. Dead code removed. Code style improved. Tests: Not run - targeted micro-optimizations to existing MethodHandle paths References: Refs #3300
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
java.lang.reflectMethod.invoke,Constructor.newInstance, andField.get/setbypass JIT inlining, adding overhead in critical paths like actor instantiation, protobuf serialization, and virtual thread management.Modification
Constructor.newInstancewithMethodHandle.invokeWithArguments; replaceField.get(null)forMODULE$withVarHandle.getConstructor.newInstancewithMethodHandle.invokefor both no-arg and parameterized constructorsField.set/getwithVarHandle.set/getfor scheduler andDEFAULT_SCHEDULERfields; replaceConstructor.newInstancewith cachedMethodHandleforCarrierThreadMethod-basedparseFrom/toByteArraycaches withMethodHandlecaches for serialization hot pathMethod.invokewithMethodHandle.invokeforwriteReplacelambda introspectionField.get(null)withVarHandle.getfortheUnsafestatic field accessMethod.invoke(null)withMethodHandle.invokefor Java singletongetInstanceaccessorResult
VarHandleandMethodHandleenable JIT inlining of field access, method invocation, and constructor calls across all core reflection utilities, improving performance in actor creation, serialization, and virtual thread operations.Tests
sbt "actor / compile"- successsbt "actor-typed / compile"- successsbt "remote / compile"- successReferences
None - internal refactoring