In both staticMethodHandle & lambdaMetafactory Dog::getName is inlined, but using different mechanisms.

In staticMethodHandle target method is statically known [1], but in case of lambdaMetafactory [2] compiler has to rely on profiling info to devirtualize Function::apply(). The latter requires exact type check on the receiver at runtime and that explains the difference you are seeing.

But comparing that with nonStaticMethodHandle is not fair: there's no inlining happening there.

I actually never dared to ask, what kind of information is really provided by the java compiler here to make the static version so fast?

Java compiler doesn't do anything special in that case. All the "magic" happens during JIT-compilation: JIT-compiler extracts method handle instance from static final field (as if it were a constant from class constant pool) and inlines through MH.invokeExact() down to the target method.

Is it because the static final version becomes a member of the class pool? Is the lambdafactory so fast, because here the handle will become the member of the pool of the generated class? And is there a way for me

In that particular case, no method handles are involved. LambdaMetafactory produces a class file w/o any method handle constants. The target method is directly referenced from bytecode [1].

to bring nonStaticMethodHandle more near to staticMethodHandle, short of making it static?

CallSites are the best you can get (JITs treat as constant and aggressively inlines through them), but you have to bind CallSite instance either to invokedynamic call site or put it into static final field.

If such scheme doesn't work for you, there's no way to match the performance of invocations on constant method handles.

The best thing you can do is to wrap method handle constant into a newly created class (put it into constant pool or static final field) and define a method which invokes the method handle constant (both indy & MH.invokeExact() work). The method should either implement a method from super-interface or overrides a method from a super-class (so there's a way to directly reference it at use sites). The latter is preferable, because invokevirtual is faster than invokeinterface. (LambdaMetafactory does the former and that's the reason it can't beat MH.invokeExact() on non-constant MH).

Best regards,
Vladimir Ivanov

final class org.lmf.LMF$$Lambda$37 implements java.util.function.Function
Constant pool:
#19 = Methodref #15.#18 // org/lmf/LMF$Dog.getName:()Ljava/lang/String;

  public java.lang.Object apply(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: (0x0001) ACC_PUBLIC
      stack=1, locals=2, args_size=2
         0: aload_1
         1: checkcast     #15                 // class org/lmf/LMF$Dog
4: invokevirtual #19 // Method org/lmf/LMF$Dog.getName:()Ljava/lang/String;
         7: areturn

mlvm-dev mailing list

Reply via email to