This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch refactor/replace-reflection-with-methodhandles
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to 
refs/heads/refactor/replace-reflection-with-methodhandles by this push:
     new ebdd3fb12e perf: cache MethodHandles and fix review issues
ebdd3fb12e is described below

commit ebdd3fb12e9241f1eb8417ce02686311ad871fe7
Author: 虎鸣 <[email protected]>
AuthorDate: Tue Jul 7 22:03:53 2026 +0800

    perf: cache MethodHandles and fix review issues
    
    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
---
 .../pekko/dispatch/VirtualThreadSupport.scala      | 63 +++++++++++++---------
 .../org/apache/pekko/io/dns/DnsSettings.scala      | 12 ++++-
 .../scala/org/apache/pekko/util/BoxedType.scala    |  2 -
 .../scala/org/apache/pekko/util/LineNumbers.scala  | 16 ++++--
 .../main/scala/org/apache/pekko/util/Reflect.scala | 15 +++---
 5 files changed, 68 insertions(+), 40 deletions(-)

diff --git 
a/actor/src/main/scala/org/apache/pekko/dispatch/VirtualThreadSupport.scala 
b/actor/src/main/scala/org/apache/pekko/dispatch/VirtualThreadSupport.scala
index 9c37e96a9f..c7ea51466c 100644
--- a/actor/src/main/scala/org/apache/pekko/dispatch/VirtualThreadSupport.scala
+++ b/actor/src/main/scala/org/apache/pekko/dispatch/VirtualThreadSupport.scala
@@ -36,6 +36,38 @@ private[dispatch] object VirtualThreadSupport {
   private val threadFactoryMethodType = 
MethodType.methodType(classOf[ThreadFactory])
   private val carrierThreadConstructorMethodType = 
MethodType.methodType(Void.TYPE, classOf[ForkJoinPool])
 
+  // Cached method/var handles to avoid repeated lookups on every invocation
+  private lazy val newThreadPerTaskExecutorHandle = {
+    val executorsClazz = 
ClassLoader.getSystemClassLoader.loadClass("java.util.concurrent.Executors")
+    lookup.findStatic(executorsClazz, "newThreadPerTaskExecutor", 
newThreadPerTaskExecutorMethodType)
+  }
+
+  private lazy val threadBuilderClass = 
ClassLoader.getSystemClassLoader.loadClass("java.lang.Thread$Builder")
+  private lazy val threadBuilderOfVirtualClass =
+    
ClassLoader.getSystemClassLoader.loadClass("java.lang.Thread$Builder$OfVirtual")
+
+  private lazy val ofVirtualHandle =
+    lookup.findStatic(classOf[Thread], "ofVirtual", 
MethodType.methodType(threadBuilderOfVirtualClass))
+
+  private lazy val ofVirtualNameHandle =
+    lookup.findVirtual(threadBuilderOfVirtualClass, "name",
+      MethodType.methodType(threadBuilderOfVirtualClass, classOf[String]))
+
+  private lazy val ofVirtualNameWithStartHandle =
+    lookup.findVirtual(threadBuilderOfVirtualClass, "name",
+      MethodType.methodType(threadBuilderOfVirtualClass, classOf[String], 
java.lang.Long.TYPE))
+
+  private lazy val builderFactoryHandle =
+    lookup.findVirtual(threadBuilderClass, "factory", threadFactoryMethodType)
+
+  private lazy val virtualThreadClass =
+    ClassLoader.getSystemClassLoader.loadClass("java.lang.VirtualThread")
+
+  private lazy val defaultSchedulerHandle =
+    MethodHandles
+      .privateLookupIn(virtualThreadClass, privateLookup)
+      .findStaticVarHandle(virtualThreadClass, "DEFAULT_SCHEDULER", 
classOf[ForkJoinPool])
+
   /**
    * Is virtual thread supported
    */
@@ -47,12 +79,7 @@ private[dispatch] object VirtualThreadSupport {
   def newThreadPerTaskExecutor(threadFactory: ThreadFactory): ExecutorService 
= {
     require(threadFactory != null, "threadFactory should not be null.")
     try {
-      val executorsClazz = 
ClassLoader.getSystemClassLoader.loadClass("java.util.concurrent.Executors")
-      val newThreadPerTaskExecutorMethod = lookup.findStatic(
-        executorsClazz,
-        "newThreadPerTaskExecutor",
-        newThreadPerTaskExecutorMethodType)
-      
newThreadPerTaskExecutorMethod.invoke(threadFactory).asInstanceOf[ExecutorService]
+      
newThreadPerTaskExecutorHandle.invoke(threadFactory).asInstanceOf[ExecutorService]
     } catch {
       case NonFatal(e) =>
         // --add-opens java.base/java.lang=ALL-UNNAMED
@@ -89,19 +116,12 @@ private[dispatch] object VirtualThreadSupport {
     require(isSupported, "Virtual thread is not supported.")
     require(prefix != null && prefix.nonEmpty, "prefix should not be null or 
empty.")
     try {
-      val builderClass = 
ClassLoader.getSystemClassLoader.loadClass("java.lang.Thread$Builder")
-      val ofVirtualClass = 
ClassLoader.getSystemClassLoader.loadClass("java.lang.Thread$Builder$OfVirtual")
-      val ofVirtualMethod = lookup.findStatic(classOf[Thread], "ofVirtual", 
MethodType.methodType(ofVirtualClass))
-      var builder = ofVirtualMethod.invoke()
+      var builder = ofVirtualHandle.invoke()
       // set the name
       if (start <= -1) {
-        val nameMethod = lookup.findVirtual(ofVirtualClass, "name",
-          MethodType.methodType(ofVirtualClass, classOf[String]))
-        builder = nameMethod.invoke(builder, prefix + "-virtual-thread")
+        builder = ofVirtualNameHandle.invoke(builder, prefix + 
"-virtual-thread")
       } else {
-        val nameMethod = lookup.findVirtual(ofVirtualClass, "name",
-          MethodType.methodType(ofVirtualClass, classOf[String], 
classOf[Long]))
-        builder = nameMethod.invoke(builder, prefix + "-virtual-thread-", zero)
+        builder = ofVirtualNameWithStartHandle.invoke(builder, prefix + 
"-virtual-thread-", zero)
       }
       // set the scheduler
       if (executor ne null) {
@@ -111,8 +131,7 @@ private[dispatch] object VirtualThreadSupport {
             classOf[Executor])
         schedulerHandle.set(builder, executor)
       }
-      val factoryMethod = lookup.findVirtual(builderClass, "factory", 
threadFactoryMethodType)
-      factoryMethod.invoke(builder).asInstanceOf[ThreadFactory]
+      builderFactoryHandle.invoke(builder).asInstanceOf[ThreadFactory]
     } catch {
       case NonFatal(e) =>
         // --add-opens java.base/java.lang=ALL-UNNAMED
@@ -139,13 +158,7 @@ private[dispatch] object VirtualThreadSupport {
   def getVirtualThreadDefaultScheduler: ForkJoinPool =
     try {
       require(isSupported, "Virtual thread is not supported.")
-      val clazz = 
ClassLoader.getSystemClassLoader.loadClass("java.lang.VirtualThread")
-      val fieldName = "DEFAULT_SCHEDULER"
-      val schedulerHandle =
-        MethodHandles
-          .privateLookupIn(clazz, privateLookup)
-          .findStaticVarHandle(clazz, fieldName, classOf[ForkJoinPool])
-      schedulerHandle.get().asInstanceOf[ForkJoinPool]
+      defaultSchedulerHandle.get().asInstanceOf[ForkJoinPool]
     } catch {
       case NonFatal(e) =>
         // --add-opens java.base/java.lang=ALL-UNNAMED
diff --git a/actor/src/main/scala/org/apache/pekko/io/dns/DnsSettings.scala 
b/actor/src/main/scala/org/apache/pekko/io/dns/DnsSettings.scala
index 4a0778b18b..2f5489797c 100644
--- a/actor/src/main/scala/org/apache/pekko/io/dns/DnsSettings.scala
+++ b/actor/src/main/scala/org/apache/pekko/io/dns/DnsSettings.scala
@@ -172,6 +172,7 @@ object DnsSettings {
   private final val DnsFallbackPort = 53
   private val inetSocketAddress = """(.*?)(?::(\d+))?""".r
   private val publicLookup = MethodHandles.publicLookup()
+  private val lookup = MethodHandles.lookup()
   private val nameserversMethodType = 
MethodType.methodType(classOf[util.List[?]])
 
   /**
@@ -239,8 +240,15 @@ object DnsSettings {
     def getNameserversUsingMethodHandles: Try[List[InetSocketAddress]] = {
       
system.dynamicAccess.getClassFor[Any]("sun.net.dns.ResolverConfiguration").flatMap
 { c =>
         Try {
-          val open = publicLookup.findStatic(c, "open", 
MethodType.methodType(c))
-          val nameservers = publicLookup.findVirtual(c, "nameservers", 
nameserversMethodType)
+          val effectiveLookup: MethodHandles.Lookup =
+            try {
+              publicLookup.findStatic(c, "open", MethodType.methodType(c))
+              publicLookup
+            } catch {
+              case _: IllegalAccessException => 
MethodHandles.privateLookupIn(c, lookup)
+            }
+          val open = effectiveLookup.findStatic(c, "open", 
MethodType.methodType(c))
+          val nameservers = effectiveLookup.findVirtual(c, "nameservers", 
nameserversMethodType)
           val instance = open.invoke()
           val ns = nameservers.invoke(instance).asInstanceOf[util.List[String]]
           val res = if (ns.isEmpty)
diff --git a/actor/src/main/scala/org/apache/pekko/util/BoxedType.scala 
b/actor/src/main/scala/org/apache/pekko/util/BoxedType.scala
index fc55f5c0d6..0e7fc09754 100644
--- a/actor/src/main/scala/org/apache/pekko/util/BoxedType.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/BoxedType.scala
@@ -26,8 +26,6 @@ object BoxedType {
     classOf[Float] -> classOf[jl.Float],
     classOf[Double] -> classOf[jl.Double],
     classOf[Unit] -> classOf[scala.runtime.BoxedUnit])
-  private val toPrimitive = toBoxed.map(_.swap)
 
   final def apply(c: Class[?]): Class[?] = if (c.isPrimitive) toBoxed(c) else c
-  final def primitiveForBoxed(c: Class[?]): Option[Class[?]] = 
toPrimitive.get(c)
 }
diff --git a/actor/src/main/scala/org/apache/pekko/util/LineNumbers.scala 
b/actor/src/main/scala/org/apache/pekko/util/LineNumbers.scala
index dab9638700..0022a96520 100644
--- a/actor/src/main/scala/org/apache/pekko/util/LineNumbers.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/LineNumbers.scala
@@ -14,7 +14,8 @@
 package org.apache.pekko.util
 
 import java.io.{ DataInputStream, InputStream }
-import java.lang.invoke.{ MethodHandles, MethodType, SerializedLambda }
+import java.lang.invoke.{ MethodHandle, MethodHandles, MethodType, 
SerializedLambda }
+import java.util.concurrent.ConcurrentHashMap
 
 import scala.annotation.switch
 import scala.util.control.NonFatal
@@ -32,6 +33,7 @@ object LineNumbers {
 
   private val lookup = MethodHandles.lookup()
   private val writeReplaceMethodType = MethodType.methodType(classOf[Object])
+  private val writeReplaceHandleCache = new ConcurrentHashMap[Class[?], 
MethodHandle]
 
   sealed trait Result
   case object NoSourceInfo extends Result
@@ -210,10 +212,16 @@ object LineNumbers {
   private def getStreamForLambda(l: AnyRef): Option[(InputStream, 
Some[String])] =
     try {
       val c = l.getClass
+      val cached = writeReplaceHandleCache.get(c)
       val writeReplaceHandle =
-        MethodHandles
-          .privateLookupIn(c, lookup)
-          .findVirtual(c, "writeReplace", writeReplaceMethodType)
+        if (cached ne null) cached
+        else {
+          val handle = MethodHandles
+            .privateLookupIn(c, lookup)
+            .findVirtual(c, "writeReplace", writeReplaceMethodType)
+          val existing = writeReplaceHandleCache.putIfAbsent(c, handle)
+          if (existing eq null) handle else existing
+        }
       writeReplaceHandle.invoke(l) match {
         case serialized: SerializedLambda =>
           if (debug)
diff --git a/actor/src/main/scala/org/apache/pekko/util/Reflect.scala 
b/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
index 4f23abf637..d0243c0846 100644
--- a/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
@@ -100,13 +100,14 @@ private[pekko] object Reflect {
 
     val key = ConstructorKey(clazz, args.map(safeGetClass).toVector)
     val cached = constructorHandles.get(key)
-    if (cached ne null) return cached
-
-    val selectedConstructorType =
-      findConstructorMethodTypeFromClassMetadata(clazz, 
args).getOrElse(error("no matching constructor"))
-    val handle = findConstructorHandle(clazz, selectedConstructorType)
-    val existing = constructorHandles.putIfAbsent(key, handle)
-    if (existing eq null) handle else existing
+    if (cached ne null) cached
+    else {
+      val selectedConstructorType =
+        findConstructorMethodTypeFromClassMetadata(clazz, 
args).getOrElse(error("no matching constructor"))
+      val handle = findConstructorHandle(clazz, selectedConstructorType)
+      val existing = constructorHandles.putIfAbsent(key, handle)
+      if (existing eq null) handle else existing
+    }
   }
 
   private def findConstructorMethodTypeFromClassMetadata[T](clazz: Class[T], 
args: immutable.Seq[Any])


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to