(Last updated Sep 2, 2018)

Hello MethodHandle

To understand what’s happening with the extra-long parameter to invokedynamic from my last post, let’s take a detour in MethodHandle.

The main reasons that I talk about MethodHandles here are:

  • Their implementation is similar to, but not as messy as, Lambdas. So it will be like a practice run before dealing with the real thing.
  • Lambda is implemented using MethodHandles, so let’s first understand how the latter works.
$ cat HelloMH.java
import java.lang.invoke.*;

public class HelloMH {
    public static void main(String ...args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(void.class, float.class);
        MethodHandle mh = lookup.findStatic(HelloMH.class, "callme", mt);
        mh.invokeExact(4.0f);
    }
 
    private static void callme(float x) {
        System.out.println("Hello MH.invoke: " + x);
        Thread.dumpStack();
    }
}
 
----

$ javac HelloMH.java
$ javap -c HelloMH.class

public class HelloMH {
 ...
 public static void main(java.lang.String[]) throws java.lang.Throwable;
  Code:
   0: invokestatic  #2    // java/lang/invoke/MethodHandles.lookup:()\
                          //   Ljava/lang/invoke/MethodHandles$Lookup;
   3: astore_1
   4: getstatic     #3    // java/lang/Void.TYPE:Ljava/lang/Class;
   7: getstatic     #4    // Field java/lang/Float.TYPE:Ljava/lang/Class;

  10: invokestatic  #5    // java/lang/invoke/MethodType.methodType:\
                          //   (Ljava/lang/Class;Ljava/lang/Class;)\
                          //    Ljava/lang/invoke/MethodType;
  13: astore_2
  14: aload_1
  15: ldc           #6    // class HelloMH
  17: ldc           #7    // String callme
  19: aload_2
  20: invokevirtual #8    // java/lang/invoke/MethodHandles$Lookup.findStatic:\
                          //   (Ljava/lang/Class;Ljava/lang/String;\
                          //    Ljava/lang/invoke/MethodType;)\
                          //    Ljava/lang/invoke/MethodHandle;
  23: astore_3
  24: aload_3             // the MethodHandle -- 1st call parameter
  25: ldc           #9    // float 4.0f       -- 2nd call parameter
  26: invokevirtual #10   // java/lang/invoke/MethodHandle.invokeExact:\
                          //   (F)V
  30: return
}
 
----
 
$ java -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames -cp . HelloMH
Hello MH.invoke: 4.0
java.lang.Exception: Stack trace
    at java.base/java.lang.Thread.dumpStack(Thread.java:1434)
    at HelloMH.callme(HelloMH.java:13)
    at java.base/java.lang.invoke.LambdaForm$DMH/0x00000007c0060840\
       .invokeStatic(LambdaForm$DMH:1000010)
    at java.base/java.lang.invoke.LambdaForm$MH/0x00000007c0061040\
       .invokeExact_MT(LambdaForm$MH:1000019)
    at HelloMH.main(HelloMH.java:8)

Note: The /0x00000007c0061040 notation printed in the stack trace indicate that LambdaForm$DMH and LambdaForm$MH are loaded as JVM anonymous classes (which are not to be confused by anonymous classes in the Java source code, ugh!).

For breveity, I will omit such notations in the rest of this page.

Compilation of MethodHandle.invokeExact by javac

MethodHandle.invokeExact looks similar to Method.invoke in the Java Reflection API. However, it’s compiled very differently by javac.

Even though the argument type of MethodHandle.invokeExact is void (Object...), the bytecodes emitted by javac looks as if you were calling the virtual method void MethodHandle.invokeExact(float), which doesn’t exist!

$ javap java.lang.invoke.MethodHandle | grep invokeExact
  public final native java.lang.Object invokeExact(java.lang.Object...)
      throws java.lang.Throwable;
$ javap -private -c HelloMH.class
...
   23: aload_3            // local 3 is the <mh> variable
   24: ldc           #9   // float 4.0f
   26: invokevirtual #10  // Method java/lang/invoke/MethodHandle.invokeExact:\
                          //   (F)V
...

To understand what’s happening, see the section Method handle compilation in the MethodHandle reference. You can also see the reference to the @PolymorphicSignature annotation in the MethodHandle.invokeExact() source code.

Execution of MethodHandle.invokeExact

To see how MethodHandle.invokeExact actually calls the target method, let’s look at the stack trace from above. Even though the bytecode is invokevirtual MethodHandle.invokeExact(...), the call frame for invokeExact is missing in the call stack, and is magically replaced by a call to a dynamically generated method java.lang.invoke.LambdaForm$MH.invokeExact_MT().

To see the contents of the generated classes, use -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true. (Note that for ease of debugging, when this flag is specified, the names of the LambdaForm$MH class is changed to LambdaForm$MH000)

$ java -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true \
    -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames \
    -cp . HelloMH
<... snip...>
Hello MH.invoke: 4.0
java.lang.Exception: Stack trace
    at java.base/java.lang.Thread.dumpStack(Thread.java:1434)
    at HelloMH.callme(HelloMH.java:13)
    at java.base/java.lang.invoke.LambdaForm$DMH000
       .invokeStatic000_LF_V()
    at java.base/java.lang.invoke.LambdaForm$MH000
       .invokeExact_MT000_LFL_V()
    at HelloMH.main(HelloMH.java:8)


$ jdis 'DUMP_CLASS_FILES/java/lang/invoke/LambdaForm$MH000.class'
package  java/lang/invoke;

super final class LambdaForm$MH000
    version 52:0
{
@+java/lang/invoke/LambdaForm$Hidden { }
@+java/lang/invoke/LambdaForm$Compiled { }
@+jdk/internal/vm/annotation/ForceInline { }
static Method invokeExact_MT000_LFL_V\
               :"(Ljava/lang/Object;FLjava/lang/Object;)V"
	stack 2 locals 3
{
  aload_0;
  checkcast     class MethodHandle;
  dup;
  astore_0;
  aload_2;
  checkcast     class MethodType;
  invokestatic  Method Invokers.checkExactType\
                :"(Ljava/lang/invoke/MethodHandle;\
                   Ljava/lang/invoke/MethodType;)V";
  aload_0;
  invokestatic  Method Invokers.checkCustomized\
                :"(Ljava/lang/invoke/MethodHandle;)V";
  aload_0;
  fload_1;
  invokevirtual  Method MethodHandle.invokeBasic:"(F)V";
  return;
}

static Method dummy:"()V"
  stack 1 locals 0
{
  ldc  String "MH.invokeExact_MT000_LFL_V=Lambda(a0:L,a1:F,a2:L)=>{\n\
         t3:V=Invokers.checkExactType(a0:L,a2:L);\n\
         t4:V=Invokers.checkCustomized(a0:L);\n\
         t5:V=MethodHandle.invokeBasic(a0:L,a1:F);void}";
  pop;
  return;
}
}

You can see that invokeExact_MT000_LFL_V first performs a few checks on its parameters, and then calls MethodHandle.invokeBasic. Well, invokeBasic is another @PolymorphicSignature method, and the call is again magically replaced by LambdaForm$DMH000.invokeStatic000_LF_V:

$ jdis 'DUMP_CLASS_FILES/java/lang/invoke/LambdaForm$DMH000.class'
...
static Method invokeStatic000_LF_V:"(Ljava/lang/Object;F)V"
    stack 2 locals 3
{
    aload_0;
    invokestatic    Method DirectMethodHandle.internalMemberName\
                       :"(Ljava/lang/Object;)Ljava/lang/Object;";
    astore_2;
    fload_1;
    aload_2;
    checkcast       class MemberName;
    invokestatic    Method MethodHandle.linkToStatic\
                        :"(FLjava/lang/invoke/MemberName;)V";
    return;
}

As you can expect, the call to MethodHandle.linkToStatic is yet again some magic inside the JVM. Basically, it’s a native method that knows the invocation target’s Method*. In our example, linkToStatic creates a call frame to execute the callme method. Note that linkToStatic itself doesn’t appear in the stack trace – it kinds of makes a tail call into callme.

References: if you’re interested in more details, see this StackOverflow answer and this HotSpot blog page.

Shuffling of Parameters

Our target method has a single declared parameter, a float, or F in the method signatures. However, you can see that some extra parameters are adding before and after the formal parameters:

  • The frame of invokeExact_MT000_LFL_V contains:
    • an object before the F: this is the MethodHandle this instance, which gets pushed to the frame because we’re making a virtual call on a MethodHandle object.
    • an object after the F: this is the MethodType. It’s appended by the invokehandle bytecode so we can dynamically check that we are invoking a MethodHandle of the expected type.

      This dynamic check is actually necessary because you can actually write valid Java source code like this which can only be checked at run time:

      import java.lang.invoke.*;
      public class BadMH {
        public static void main(String ...args) throws Throwable {
          MethodHandle mh[] = new MethodHandle[2];
          MethodHandles.Lookup lookup = MethodHandles.lookup();
          MethodType mt0 = MethodType.methodType(void.class, float.class);
          mh[0] = lookup.findStatic(BadMH.class, "callme", mt0);
          MethodType mt1 = MethodType.methodType(void.class, Object.class);
          mh[1] = lookup.findStatic(BadMH.class, "callme", mt1);
          for (MethodHandle x : mh) {
            x.invokeExact(4.0f);
         }
        }
      
        private static void callme(float x) {
          System.out.println("Hello MH.invoke: " + x);
        }
        private static void callme(Object x) {
          System.out.println("Hello MH.invoke: " + x);
        }
      }
      $ java -cp . BadMH
      Hello MH.invoke: 4.0
      java.lang.invoke.WrongMethodTypeException:
            expected (Object)void but found (float)void
        at java.lang.invoke.Invokers.newWrongMethodTypeException\
           (Invokers.java:476)
        at java.lang.invoke.Invokers.checkExactType(Invokers.java:485)
        at BadMH.main(BadMH.java:12)
      
  • The frame of invokeStatic000_LF_V contains:
    • an object before the F: this is the MemberName that represents the target method (it basically carries a C++ Method pointer to the target method).

Inside invokeStatic000_LF_V, we shuffle the parameters such that the incoming call frame of MethodHandle.linkToStatic contains:

  • all the parameters as declared by the target method (a single F in our example)
  • followed by the MemberName of the target method.

When the native method MethodHandle.linkToStatic is entered, the “front” part of the call stack already contains the exact parameters as needed by the target method. linkToStatic simply retrieves the JVM Method pointer from the MemberName, drops this last parameter, and jumps to the entry of the target method.

(We’ll see this in more details in the Linking Polymorphic Methods sections below.)

MethodHandle vs varargs

As we saw above, the arguments to MethodHandle.invokeExact are not passed using the Varargs convention (unlike java.lang.reflect.Method.invoke which is Varargs and would create an Object array to collect the arguments, and thus would be slower and creat lots of garbage.) Here’s an example of varargs for comparison:

public class HelloVarargs {
    public static void main(String... args) {
        if (args.length == 0) {
            main("a", "b"); // compiled as:
                            //     main(new String[] {"a", "b"});
        }
    }
}
---
$ javap -c HelloVarargs.class
...
  public static void main(java.lang.String...);
    Code:
       0: aload_0
       1: arraylength
       2: ifne          22
       5: iconst_2
       6: anewarray     #2  // class java/lang/String
          ^^^^^^^ create a String array of 2 elements

       9: dup
      10: iconst_0
      11: ldc           #3  // String a
      13: aastore
      14: dup
      15: iconst_1
      16: ldc           #4  // String b
      18: aastore
      19: invokestatic  #5  // Method main:([Ljava/lang/String;)V
      22: return

Benchmarking MethodHandles

Here’s a benchmark of java.lang.invoke.MethodHandle vs java.lang.reflect.Method:

$ cat BenchMH.java
import java.lang.invoke.*;
import java.lang.reflect.*;
 
public class BenchMH {
  static class Helper {
    static MethodHandle getHandle() {
      try {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(void.class, String.class);
        return lookup.findStatic(BenchMH.class, "callme", mt);
      } catch (Throwable t) {
        t.printStackTrace();
        return null;
      }
    }
    static Method getMethod() {
      try {
        Class[] argTypes = new Class[] { String.class };
        return BenchMH.class.getDeclaredMethod("callme", argTypes);
      } catch (Throwable t) {
        t.printStackTrace();
        return null;
      }
    }
  }

  static String c;
  static final MethodHandle mh = Helper.getHandle();
  static final Method m = Helper.getMethod();

  public static void main(String args[]) throws Throwable {
    int loops = 400 * 1000 * 1000;
    try {
      loops = Integer.parseInt(args[0]);
    } catch (Throwable t) {}
 
    Class[] argTypes = new Class[] { String.class };
    Method m = BenchMH.class.getDeclaredMethod("callme", argTypes);
 
    // The second loop (hopefully) excludes warm up time.
    for (int x=0; x<2; x++) {
      {
      long start = System.currentTimeMillis();
      loopMH(loops);
      long end = System.currentTimeMillis();
      System.out.println("MH:    loops = " + loops + ": elapsed = "
        + (end - start) + " ms");
      }
      {
      long start = System.currentTimeMillis();
      loopReflect(loops);
      long end = System.currentTimeMillis();
      System.out.println("Reflect: loops = " + (loops) + ": elapsed = "
        + (end - start) + " ms");
      }
      {
      long start = System.currentTimeMillis();
      loopDirect(loops);
      long end = System.currentTimeMillis();
      System.out.println("direct:  loops = " + loops + ": elapsed = "
        + (end - start) + " ms");
      }
    }
  }
 
  private static void loopMH(int loops) throws Throwable {
    for (int i=0; i<loops; i++) {
      mh.invokeExact("yo!");
    }
  }
 
  private static void loopReflect(int loops) throws Throwable {
    for (int i=0; i<loops; i++) {
      m.invoke(null, "yo!");
    }
  }
 
 
  private static void loopDirect(int loops) {
    for (int i=0; i<loops; i++) {
      callme("yo!");
    }
  }
 
  private static void callme(String x) {
    c = x;
  }
}

$ java BenchMH 100000000

           LOOPS     ELAPSED 
MH:      400000000   443 ms
Reflect: 400000000  1054 ms  ... 2.3x slower than MH
direct:  400000000   435 ms

Note that by using static final MethodHandle mh, we can get the JIT to inline the method handle invocation, so it’s just as fast as a direct method invocation. But static final is not very convenient to use, and the JDK uses the @Stable annotation to achieve the same result. You can see an example here. However, @Stable can be used only by classes loaded by the bootstrap loader.

Linking Polymorphic Methods in the HotSpot JVM (static invocations)

Let’s look at the easy ones first. A few native polymorphic methods (declared with the @PolymorphicSignature annotation) in the MethodHandle class are invoked statically by the generated MethodHandle invoker classes:

  • invokeGeneric
  • invokeBasic
  • linkToVirtual
  • linkToStatic
  • linkToSpecial
  • linkToInterface

Here’s an example we have seen above:

static Method invokeStatic000_LF_V:"(Ljava/lang/Object;F)V"
  ...
  invokestatic MethodHandle.linkToStatic:"(FLjava/lang/invoke/MemberName;)V";

These calls are resolved and linked conventionally just like other native static methods. The only interesting part is these methods are implemented using assembler code, so you won’t see a C function for Java_java_lang_invoke_MethodHandle_linkToStatic inside libjava.so.

On x64, the imterpreter’s implementation of MethodHandle.linkToStatic is generated around here in MethodHandles::generate_method_handle_interpreter_entry. It contains only 10 instructions:

(gdb) x/10i 0x7fffd8e5a920
   // pop(rax_temp)   --  return address
   0x7fffd8e5a920: 	pop    %rax
   // pop(rbx_member) --  extract last argument (MemberName)
   0x7fffd8e5a921:	pop    %rbx
   // push(rax_temp)  -- re-push return address
   0x7fffd8e5a922:	push   %rax

   // load_heap_oop(rbx_method, member_vmtarget)
   0x7fffd8e5a923:	mov    0x24(%rbx),%ebx
   0x7fffd8e5a926:	shl    $0x3,%rbx        // decode compressed oop

   // movptr(rbx_method, vmtarget_method);
   0x7fffd8e5a92a:	mov    0x10(%rbx),%rbx

   // testptr(rbx, rbx); jcc(Assembler::zero, L_no_such_method);
   0x7fffd8e5a92e:	test   %rbx,%rbx
   0x7fffd8e5a931:	je     0x7fffd8e5a93a    // Lno_such_method

   // jmp(Address(method, entry_offset));
   0x7fffd8e5a937:	jmpq   *0x48(%rbx)

   // bind(L_no_such_method);
   // jump(RuntimeAddress(StubRoutines::throw_AbstractMethodError_entry()));
   0x7fffd8e5a93a:	jmpq   0x7fffd8e5a280

As discussed previously, linkToStatic simply pops off the last parameter as a MemberName, such that the incoming parameters are exactly what the target method wants, and then branch to the target method (as a tail call).

Linking Polymorphic Methods in the HotSpot JVM (Virtual Invocations)

Vitrual invocations (using the invokevirtual or invokespecial bytecodes) of polymorphic methods are much more complicated, and involve lots of Java code.

Recall that a polymorphic method such as MethodHandle.invokeExact is compiled by javac into the following bytecodes inside the classfile.

  23: aload_3             // the MethodHandle -- 1st call parameter
  24: ldc           #9    // String yo!       -- 2nd call parameter
  26: invokevirtual #10   // java/lang/invoke/MethodHandle.invokeExact:\
                          //   (Ljava/lang/String;)V

When the classfile is loaded by HotSpot, the invokevirtual bytecode is rewritten into the internal invokehandle bytecode in here:

->  (*opc) = (u1)Bytecodes::_invokehandle;

(gdb) where
#0 Rewriter::maybe_rewrite_invokehandle()    @ rewriter.cpp:240
#1 Rewriter::rewrite_member_reference()      @ rewriter.cpp:174
#2 Rewriter::scan_method()                   @ rewriter.cpp:472
#3 Rewriter::rewrite_bytecodes()             @ rewriter.cpp:550
#4 Rewriter::Rewriter()                      @ rewriter.cpp:590
#5 Rewriter::rewrite()                       @ rewriter.cpp:572
#6 InstanceKlass::rewrite_class()            @ instanceKlass.cpp:655
#7 InstanceKlass::link_class_impl()          @ instanceKlass.cpp:605
...

When the invokehandle bytecode is executed for the first time, it’s linked here by calling back into Java.

    // call java.lang.invoke.MethodHandleNatives::linkMethod(... String,
    //              MethodType) -> MemberName
    JavaCallArguments args;
    args.push_oop(Handle(THREAD, accessing_klass->java_mirror()));
    args.push_int(ref_kind);
    args.push_oop(Handle(THREAD, klass->java_mirror()));
    args.push_oop(name_str);
    args.push_oop(method_type);
    args.push_oop(appendix_box);
    JavaValue result(T_OBJECT);
->  JavaCalls::call_static(&result,
                           SystemDictionary::MethodHandleNatives_klass(),
                           vmSymbols::linkMethod_name(),
                           vmSymbols::linkMethod_signature(),
                           &args, CHECK_(empty));

(gdb) where
#0 SystemDictionary::find_method_handle_invoker() @ systemDictionary.cpp:2556
#1 LinkResolver::lookup_polymorphic_method()      @ linkResolver.cpp:519
#2 LinkResolver::resolve_handle_call()            @ linkResolver.cpp:1661
#3 LinkResolver::resolve_invokehandle()           @ linkResolver.cpp:1647
#4 LinkResolver::resolve_invoke()                 @ linkResolver.cpp:1573
#5 InterpreterRuntime::resolve_invokehandle()     @ interpreterRuntime.cpp:968
#6 InterpreterRuntime::resolve_from_cache()       @ interpreterRuntime.cpp:1016

When the MethodHandleNatives.linkMethod call completes, a MemberName is returned (see here).

At this point, the linking of this polymorphic method call is complete. Subsequently, this invokehandle bytecode can be executed by loading information about the MethodHandle from the corresponding ConstantPoolCacheEntry.

TODO …

I can probably include more details here, but I’ll skip them for now …

Summary

Now you know how a MethodHandle is invoked. In the next blog entry, we’ll see how MethodHandles are used by the invokedynamic bytecode.