Hi, I just noticed an opportunity to optimize Class.toString() for primitive types.
I've written a small benchmark and ran it against JDK 15 vs a patched variant. @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class MyBenchmark { @State(Scope.Benchmark) public static class ThreadState { private Class<?> primitive = long.class; private Class<?> clazz = Long.class; private Class<?> interfaze = Map.class; } @Benchmark public String testPrimitive(ThreadState threadState) { return threadState.primitive.toString(); } @Benchmark public String testClass(ThreadState threadState) { return threadState.clazz.toString(); } @Benchmark public String testInterface(ThreadState threadState) { return threadState.interfaze.toString(); } } This yields the following results: Before Benchmark Mode Cnt Score Error Units MyBenchmark.testClass avgt 10 32,268 ± 2,961 ns/op MyBenchmark.testClass:·gc.alloc.rate avgt 10 3601,964 ± 336,786 MB/sec MyBenchmark.testClass:·gc.alloc.rate.norm avgt 10 152,009 ± 0,001 B/op MyBenchmark.testClass:·gc.count avgt 10 203,000 counts MyBenchmark.testClass:·gc.time avgt 10 138,000 ms MyBenchmark.testInterface avgt 10 37,685 ± 3,728 ns/op MyBenchmark.testInterface:·gc.alloc.rate avgt 10 3086,794 ± 309,983 MB/sec MyBenchmark.testInterface:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op MyBenchmark.testInterface:·gc.count avgt 10 160,000 counts MyBenchmark.testInterface:·gc.time avgt 10 107,000 ms MyBenchmark.testPrimitive avgt 10 20,937 ± 2,668 ns/op MyBenchmark.testPrimitive:·gc.alloc.rate avgt 10 3809,437 ± 470,140 MB/sec MyBenchmark.testPrimitive:·gc.alloc.rate.norm avgt 10 104,006 ± 0,001 B/op MyBenchmark.testPrimitive:·gc.count avgt 10 215,000 counts MyBenchmark.testPrimitive:·gc.time avgt 10 167,000 ms After Benchmark Mode Cnt Score Error Units MyBenchmark.testClass avgt 10 31,585 ± 5,365 ns/op MyBenchmark.testClass:·gc.alloc.rate avgt 10 3704,714 ± 549,224 MB/sec MyBenchmark.testClass:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op MyBenchmark.testClass:·gc.count avgt 10 191,000 counts MyBenchmark.testClass:·gc.time avgt 10 139,000 ms MyBenchmark.testInterface avgt 10 34,534 ± 2,073 ns/op MyBenchmark.testInterface:·gc.alloc.rate avgt 10 3358,401 ± 193,391 MB/sec MyBenchmark.testInterface:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op MyBenchmark.testInterface:·gc.count avgt 10 173,000 counts MyBenchmark.testInterface:·gc.time avgt 10 131,000 ms MyBenchmark.testPrimitive avgt 10 2,829 ± 0,139 ns/op MyBenchmark.testPrimitive:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec MyBenchmark.testPrimitive:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op MyBenchmark.testPrimitive:·gc.count avgt 10 ≈ 0 counts I don't think the patched version is actually faster for classes & interfaces; I guess it's rather a draw for those. Yet, for primitives we can see a 10x improvement due to the avoided string concats. In case you think this is worthwhile I would need someone to sponsor this patch. I would highly appreciate that. Let me know what you think Cheers, Christoph ===== PATCH ===== diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -196,8 +196,11 @@ * @return a string representation of this {@code Class} object. */ public String toString() { - return (isInterface() ? "interface " : (isPrimitive() ? "" : "class ")) - + getName(); + String name = getName(); + if (isPrimitive()) { + return name; + } + return (isInterface() ? "interface " : "class ") + name; }