RFR: 8366424: Missing type profiling in generated Record Object methods
Jorn Vernee
jvernee at openjdk.org
Fri Oct 10 16:32:45 UTC 2025
On Fri, 26 Sep 2025 22:13:00 GMT, Chen Liang <liach at openjdk.org> 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
More information about the core-libs-dev
mailing list