On Fri, 26 Sep 2025 22:13:00 GMT, Chen Liang <[email protected]> wrote:
> Hotspot profiles by bytecode; as a result, some shared methods become > polluted and suffer in type profiling, as described in depth in [this > essay](https://cr.openjdk.org/~jrose/jvm/equals-profile.html) by John Rose. > The record methods generated by `ObjectMethods::bootstrap` just proved itself > another victim in this RFE. > > To bypass this issue, I naively generated distinct bytecode to allow distinct > profiles for now. If hotspot adds any kind of split profiles exposed via > internal APIs, we can migrate to such split profile and throw away these > extra copies of bytecode. > > In particular, in a method handle tree, each leaf method handle seems not > separately profiled - for example, all DMH to Object.hashCode share the same > profile regardless of their position in a MH tree, making MH trees less > useful than explicitly rolled bytecode, unfortunately. > > The attached benchmark should be a good demonstration of the effect of type > profiling. Looks mostly good. src/java.base/share/classes/java/lang/runtime/ObjectMethods.java line 178: > 176: private static boolean isMonomorphic(Class<?> type) { > 177: // Includes primitives and final classes > 178: return Modifier.isFinal(type.getModifiers()) && !type.isArray(); Why are arrays excluded here? Could you add a comment? src/java.base/share/classes/java/lang/runtime/ObjectMethods.java line 213: > 211: var type = getter.type().returnType(); > 212: if (isMonomorphic(type)) { > 213: equalators[i] = equalator(lookup, type); Do we also have tests that test this code path now? `final` class seems like it might be rare in tests. src/java.base/share/classes/java/lang/runtime/ObjectMethods.java line 250: > 248: .aload(0) // arg0.equals(arg1) - bytecode > subject to customized profiling > 249: .aload(1) > 250: .invoke(isInterface ? > Opcode.INVOKEINTERFACE : Opcode.INVOKEVIRTUAL, typeDesc, "equals", > MethodTypeDesc.of(CD_boolean, CD_Object), isInterface) Could factor out the MethodTypeDesc here. test/micro/org/openjdk/bench/java/lang/runtime/RecordMethodsBenchmark.java line 51: > 49: @OutputTimeUnit(TimeUnit.MICROSECONDS) > 50: @BenchmarkMode(Mode.Throughput) > 51: public class RecordMethodsBenchmark { I think it would be interesting to add another case here where the records fields are monomorphic, to see how that compares against the specialized case. ------------- PR Review: https://git.openjdk.org/jdk/pull/27533#pullrequestreview-3324486245 PR Review Comment: https://git.openjdk.org/jdk/pull/27533#discussion_r2421088849 PR Review Comment: https://git.openjdk.org/jdk/pull/27533#discussion_r2421076291 PR Review Comment: https://git.openjdk.org/jdk/pull/27533#discussion_r2421006192 PR Review Comment: https://git.openjdk.org/jdk/pull/27533#discussion_r2421111513
