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

Reply via email to