Hello, from discussion in https://github.com/openjdk/jdk/pull/3464 it appears, that in `j.l.Class` expressions like
String str = baseName.replace('.', '/') + '/' + name; are not compiled into invokedynamic-based code, but into one using `StringBuilder`. This happens due to some bootstraping issues. Currently the bytecode for the last (most often used) branch of `Class.descriptorString()` looks like public sb()Ljava/lang/String; L0 LINENUMBER 21 L0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ASTORE 1 L1 LINENUMBER 23 L1 ALOAD 1 LDC "a" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L2 LINENUMBER 24 L2 ALOAD 1 LDC "b" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; POP L3 LINENUMBER 26 L3 ALOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ARETURN Here the `StringBuilder` is created with default constructor and then expands if necessary while appending. This can be improved by manually allocating `StringBuilder` of exact size. The benchmark demonstrates measurable improvement: @State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"}) public class ClassDescriptorStringBenchmark { private final Class<?> clazzWithShortDescriptor = Object.class; private final Class<?> clazzWithLongDescriptor = getClass(); @Benchmark public String descriptorString_short() { return clazzWithShortDescriptor.descriptorString(); } @Benchmark public String descriptorString_long() { return clazzWithLongDescriptor.descriptorString(); } } original -Xint Mode Score Error Units descriptorString_long avgt 6326.478 ± 107.251 ns/op descriptorString_short avgt 5220.729 ± 103.545 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 528.089 ± 0.021 B/op descriptorString_short:·gc.alloc.rate.norm avgt 232.036 ± 0.015 B/op -XX:TieredStopAtLevel=1 Mode Score Error Units descriptorString_long avgt 230.223 ± 1.254 ns/op descriptorString_short avgt 164.255 ± 0.755 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 528.046 ± 0.002 B/op descriptorString_short:·gc.alloc.rate.norm avgt 232.022 ± 0.001 B/op full Mode Score Error Units descriptorString_long avgt 74.835 ± 0.262 ns/op descriptorString_short avgt 43.822 ± 0.788 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 504.010 ± 0.001 B/op descriptorString_short:·gc.alloc.rate.norm avgt 208.004 ± 0.001 B/op ------------------------ patched -Xint Mode Score Error Units descriptorString_long avgt 4485.994 ± 60.173 ns/op descriptorString_short avgt 3949.965 ± 278.143 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 336.051 ± 0.004 B/op descriptorString_short:·gc.alloc.rate.norm avgt 184.027 ± 0.010 B/op -XX:TieredStopAtLevel=1 Mode Score Error Units descriptorString_long avgt 185.774 ± 1.100 ns/op descriptorString_short avgt 135.338 ± 1.066 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 336.030 ± 0.001 B/op descriptorString_short:·gc.alloc.rate.norm avgt 184.019 ± 0.001 B/op full Mode Score Error Units descriptorString_long avgt 42.864 ± 0.160 ns/op descriptorString_short avgt 27.255 ± 0.381 ns/op descriptorString_long:·gc.alloc.rate.norm avgt 224.005 ± 0.001 B/op descriptorString_short:·gc.alloc.rate.norm avgt 120.002 ± 0.001 B/op Same can be done also for Class.isHidden() branch in Class.descriptorString() and for Class.getCanonicalName0() ------------- Commit messages: - 8266622: Optimize Class.descriptorString() and Class.getCanonicalName0() Changes: https://git.openjdk.java.net/jdk/pull/3903/files Webrev: https://webrevs.openjdk.java.net/?repo=jdk&pr=3903&range=00 Issue: https://bugs.openjdk.java.net/browse/JDK-8266622 Stats: 19 lines in 1 file changed: 15 ins; 0 del; 4 mod Patch: https://git.openjdk.java.net/jdk/pull/3903.diff Fetch: git fetch https://git.openjdk.java.net/jdk pull/3903/head:pull/3903 PR: https://git.openjdk.java.net/jdk/pull/3903