The invokedynamic uses method handles via the bootstrap methods mechanism. Unlike invokevirtual, invokedynamic instructions have no receiver object. Instead, they act like invokestatic, and use a BSM to return an object of type CallSite.
A: invokedynamic is a bytecode instruction that facilitates the implementation of dynamic languages (for the JVM) through dynamic method invocation. This instruction is described in the Java SE 7 Edition of the JVM Specification.
Boostrap Method is a method which instantiates CallSite objects. Each invokedynamic has a known bootstrap method given as its compile-time parameter. Whenever a invokedynamic is processed for a first time, appropriate bootstrap method is invoked. As result of boostrap method execution a CallSite object is created.
The invokedynamic instruction is used to help the VM determine the method reference at runtime instead hardwiring it at compile time. This is useful with dynamic languages where the exact method and argument types aren't known until runtime. But that isn't the case with Java lambdas.
It is a new JVM instruction which allows a compiler to generate code which calls methods with a looser specification than was previously possible -- if you know what "duck typing" is, invokedynamic basically allows for duck typing. There's not too much you as a Java programmer can do with it; if you're a tool creator, though, you can use it to build more flexible, more efficient JVM-based languages. Here is a really sweet blog post that gives a lot of detail.
As part of my Java Records article, I articulated about the motivation behind Invoke Dynamic. Let's start with a rough definition of Indy.
Invoke Dynamic (Also known as Indy) was part of JSR 292 intending to enhance the JVM support for Dynamic Type Languages. After its first release in Java 7, The invokedynamic
opcode along with its java.lang.invoke
luggage is used quite extensively by dynamic JVM-based languages like JRuby.
Although indy specifically designed to enhance the dynamic language support, it offers much more than that. As a matter of fact, it’s suitable to use wherever a language designer needs any form of dynamicity, from dynamic type acrobatics to dynamic strategies!
For instance, the Java 8 Lambda Expressions are actually implemented using invokedynamic
, even though Java is a statically typed language!
For quite some time JVM did support four method invocation types: invokestatic
to call static methods, invokeinterface
to call interface methods, invokespecial
to call constructors, super()
or private methods and invokevirtual
to call instance methods.
Despite their differences, these invocation types share one common trait: we can’t enrich them with our own logic. On the contrary, invokedynamic
enables us to Bootstrap the invocation process in any way we want. Then the JVM takes care of calling the Bootstrapped Method directly.
The first time JVM sees an invokedynamic
instruction, it calls a special static method called Bootstrap Method. The bootstrap method is a piece of Java code that we’ve written to prepare the actual to-be-invoked logic:
Then the bootstrap method returns an instance of java.lang.invoke.CallSite
. This CallSite
holds a reference to the actual method, i.e. MethodHandle
.
From now on, every time JVM sees this invokedynamic
instruction again, it skips the Slow Path and directly calls the underlying executable. The JVM continues to skip the slow path unless something changes.
Java 14 Records
are providing a nice compact syntax to declare classes that are supposed to be dumb data holders.
Considering this simple record:
public record Range(int min, int max) {}
The bytecode for this example would be something like:
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn
In its Bootstrap Method Table:
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I
So the bootstrap method for Records is called bootstrap
which resides in the java.lang.runtime.ObjectMethods
class. As you can see, this bootstrap method expects the following parameters:
MethodHandles.Lookup
representing the lookup context
(The Ljava/lang/invoke/MethodHandles$Lookup
part).toString
, equals
, hashCode
, etc.) the bootstrap
is going to link. For example, when the value is toString
, bootstrap
will return a ConstantCallSite
(a CallSite
that never changes) that
points to the actual toString
implementation for this particular
Record.TypeDescriptor
for the method (Ljava/lang/invoke/TypeDescriptor
part).Class<?>
, representing the Record class type. It’s
Class<Range>
in this case.min;max
.MethodHandle
per component. This way the bootstrap method can
create a MethodHandle
based on the components for this particular
method implementation.The invokedynamic
instruction passes all those arguments to the bootstrap method. Bootstrap method, in turn, returns an instance of ConstantCallSite
. This ConstantCallSite
is holding a reference to requested method implementation, e.g. toString
.
As opposed to the Reflection APIs, the java.lang.invoke
API is quite efficient since the JVM can completely see through all invocations. Therefore, JVM may apply all sorts of optimizations as long as we avoid the slow path as much as possible!
In addition to the efficiency argument, the invokedynamic
approach is more reliable and less brittle because of its simplicity.
Moreover, the generated bytecode for Java Records is independent of the number of properties. So, less bytecode and faster startup time.
Finally, let’s suppose a new version of Java includes a new and more efficient bootstrap method implementation. With invokedynamic
, our app can take advantage of this improvement without recompilation. This way we have some sort of Forward Binary Compatibility. Also, That’s the dynamic strategy we were talking about!
In addition to Java Records, the invoke dynamic has been used to implement features like:
LambdaMetafactory
StringConcatFactory
Some time ago, C# added a cool feature, dynamic syntax within C#
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Think of it as syntax sugar for reflective method calls. It can have very interesting applications. see http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, who's responsible for C#'s dynamic type, just defected from SUN to MS. So it's not unreasonable to think that the same things had been discussed inside SUN.
I remember soon after that, some Java dude announced something similar
InvokeDynamic duck = obj;
duck.quack();
Unfortunately, the feature is no where to be found in Java 7. Very disappointed. For Java programmers, they have no easy way to take advantage of invokedynamic
in their programs.
There are two concepts to understand before continuing to invokedynamic.
1. Static vs. Dynamic Typing
Static - preforms type checking at compile time (e.g. Java)
Dynamic - preforms type checking at runtime (e.g. JavaScript)
Type checking is a process of verifying that a program is type safe, this is, checking typed information for class and instance variables, method parameters, return values, and other variables. E.g. Java knows about int, String,.. at compile time, while type of an object in JavaScript can only be determined at runtime
2. Strong vs. Weak typing
Strong - specifies restrictions on the types of values supplied to its operations (e.g. Java)
Weak - converts (casts) arguments of an operation if those arguments have incompatible types (e.g. Visual Basic)
Knowing that Java is a Statically and Weakly typed, how do you implement Dynamically and Strongly typed languages on the JVM?
The invokedynamic implements a runtime system that can choose the most appropriate implementation of a method or function — after the program has been compiled.
Example: Having (a + b) and not knowing anything about the variables a,b at compile time, invokedynamic maps this operation to the most appropriate method in Java at runtime. E.g., if it turns out a,b are Strings, then call method(String a, String b). If it turns out a,b are ints, then call method(int a, int b).
invokedynamic was introduced with Java 7.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With