Только вариант MethodHandle static final
рассматривается как константа JIT, см., Например, ciField :
// Is this field a constant?
//
// Clarification: A field is considered constant if:
// 1. The field is both static and final
// 2. The field is not one of the special static/final
// non-constant fields. These are java.lang.System.in
// and java.lang.System.out. Abomination.
//
// A field is also considered constant if
// - it is marked @Stable and is non-null (or non-zero, if a primitive) or
// - it is trusted or
// - it is the target field of a CallSite object.
//
// See ciField::initialize_from() for more details.
//
// A user should also check the field value (constant_value().is_valid()), since
// constant fields of non-initialized classes don't have values yet.
bool is_constant() const { return _is_constant; }
И только вызовы через MethodHandles, которые являются постоянными, являются встроеннымисм. CallGenerator::for_method_handle_inline
Где он делает несколько проверок, чтобы увидеть, что получатель постоянен, как:
Node* receiver = kit.argument(0);
if (receiver->Opcode() == Op_ConP) {
...
} else {
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
"receiver not constant");
}
Это различие делает так, что вызов static final
MethodHandle может бытьвстроенный и, следовательно, примерно такой же быстрый, как и простой случай.
Если вы распечатываете встроенную информацию, вы также можете увидеть это.Например, вы можете добавить что-то вроде:
@Fork(jvmArgsAppend="-Xlog:inlining*=trace:inlining-%p-static_mh_invokeExact.txt")
К методам тестирования.
В статическом случае вы увидите, что вызов встроен:
@ 17 org.sample.MyBenchmark::static_mh_invokeExact (8 bytes) force inline by CompileCommand
@ 4 java.lang.invoke.LambdaForm$MH/0x00000008000f0040::invokeExact_MT (23 bytes) force inline by annotation
@ 10 java.lang.invoke.Invokers::checkExactType (17 bytes) force inline by annotation
@ 1 java.lang.invoke.MethodHandle::type (5 bytes)
@ 14 java.lang.invoke.Invokers::checkCustomized (23 bytes) force inline by annotation
@ 1 java.lang.invoke.MethodHandleImpl::isCompileConstant (2 bytes)
@ 19 java.lang.invoke.LambdaForm$MH/0x00000008000f0440::getInt (34 bytes) force inline by annotation
@ 7 java.lang.invoke.DirectMethodHandle::fieldOffset (9 bytes) force inline by annotation
@ 12 java.lang.invoke.DirectMethodHandle::checkBase (5 bytes) force inline by annotation
@ 1 java.util.Objects::requireNonNull (14 bytes)
@ 8 java.lang.NullPointerException::<init> (5 bytes) don't inline Throwable constructors
@ 30 jdk.internal.misc.Unsafe::getInt (0 bytes) intrinsic
Мы 'повторяю весь путь до вызова Unsafe::getInt
(но важная часть в том, что мы видим @ 19 java.lang.invoke.LambdaForm$MH/0x00000008000f0440::getInt
вместо invokeBasic
).
В динамическом случае вы в большинстве случаев увидите:
@ 17 org.sample.MyBenchmark::dynamic_mh_invokeExact (8 bytes) force inline by CompileCommand
@ 4 java.lang.invoke.LambdaForm$MH/0x00000008000f0040::invokeExact_MT (23 bytes) force inline by annotation
@ 10 java.lang.invoke.Invokers::checkExactType (17 bytes) force inline by annotation
@ 1 java.lang.invoke.MethodHandle::type (5 bytes)
@ 12 java.lang.invoke.Invokers::newWrongMethodTypeException (36 bytes) callee is too large
@ 14 java.lang.invoke.Invokers::checkCustomized (23 bytes) force inline by annotation
@ 1 java.lang.invoke.MethodHandleImpl::isCompileConstant (2 bytes)
@ 19 java.lang.invoke.Invokers::maybeCustomize (28 bytes) don't inline by annotation
@ 19 java.lang.invoke.MethodHandle::invokeBasic(L)I (0 bytes) receiver not constant
Т.е. в этом случае все еще существует косвенный вызов через заглушку invokeBasic
,потому что "приемник не постоянен".