20 Method arguments:
21 #25 ()Ljava/lang/Object;
22 #26 invokestatic Listing2.lambdiseMe:()Ljava/lang/String;
23 #27 ()Ljava/lang/String;
25 public static java.lang.String lambdiseMe();
26 descriptor: ()Ljava/lang/String;
28 stack=1, locals=0, args_size=0
29 0: ldc #2 // String Hello, World!
30 2: areturn
32 public static java.util.function.Supplier<java.lang.String> getSupplier();
33 descriptor: ()Ljava/util/function/Supplier;
35 stack=1, locals=0, args_size=0
36 0: invokedynamic #3, 0
// InvokeDynamic #0:get:()Ljava/util/function/Supplier;
37 5: areturn
The most familiar fragment of the javap results is the lambdiseMe method. It is a static method with no
arguments and no locals. This method pushes constant #2 from the constant pool onto the stack using the
ldc command. From the comment, we can see that the constant is the String value “Hello, World!” The code
then returns the top value of the stack by calling areturn : previously, we always saw void methods, and so
they called return instead of areturn . But, as we can see from the descriptor, we are going to return a value
of type String, and so we use areturn .
The getSupplier method is of the same basic form of the lambdiseMe method: it executes a command
to push an object onto the stack, and then it returns the object on that stack. The difference is the command
that pushes the object onto the stack. In lambdiseMe , it is loading a constant. In getSupplier, the value for the
stack comes from this strange call, invokedynamic .
The invokedynamic instruction was introduced in Java 7 to support dynamic languages that ran on
the JVM, such as JRuby and Groovy. These dynamic languages had a similar problem: for certain common
method calls, they could not know exactly what method to call until runtime. No amount of polymorphism
helped them out: they simply had to defer the decision about what method to run until the moment the code
was executed. The result was that each dynamic language had its own method dispatch structure built on
top of Java's own method dispatch structure, and those structures tended to be bad for Java's optimizations.
To help them out (and lay the groundwork for lambdas), Java 7 introduced invokedynamic .
The invokedynamic instruction tells Java to wait until runtime, and then resolve the method name when
the invokedynamic instruction is first encountered. When the invokedynamic instruction is encountered,
it will delegate to another method provided by the classfile, called the “bootstrap method.” The bootstrap
method will get a bunch of information about the call site, and may also take some extra constants. The
bootstrap method will be expected to return the method binding for that location, which is encapsulated in
the type java.lang.invoke.CallSite . That CallSite has all the information that the JVM needs to dispatch
that method: from that point forward, when that particular invokedynamic instruction is encountered, the
JVM will execute the method as specified by the CallSite .