Java 7 release introduced a new invokedynamic bytecode instruction to the Java Virtual Machine (JVM) and a new java.lang.invoke API package to the standard class library. 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.

1 Invokedynamic

In a dynamic language, type-checking typically occurs at runtime. Developers must pass appropriate types or risk runtime failures. It's often the case that java.lang.Object is the most accurate type for a method argument. This situation complicates type checking, which impacts performance.

Another challenge is that dynamic languages typically offer the capability to add fields/methods to and remove them from existing classes. As a result, it's necessary to defer class, method, and field resolution to runtime. Also, it's often necessary to adapt a method invocation to a target that has a different signature.

These challenges have traditionally required ad hoc runtime support to be built on top of the JVM. This support includes wrapper type classes, using hash tables to provide dynamic symbol resolution, and so on. Bytecode is generated with entry points to the runtime in the form of method calls using any of the four method-invocation instructions:

  • invokestatic is used to invoke static methods.
  • invokevirtual is used to invoke public and protected non-static methods via dynamic dispatch.
  • invokeinterface is similar to invokevirtual except for the method dispatch being based on an interface type.
  • invokespecial is used to invoke instance initialization methods (constructors) as well as private methods and methods of a superclass of the current class.

This runtime support affects performance. Generated bytecode often requires several actual JVM method invocations for one dynamic language method invocation. Reflection is widely used and contributes to performance degradation. Also, the many different execution paths make it impossible for the JVM's just-in-time (JIT) compiler to apply optimizations.

To address poor performance, the invokedynamic instruction does away with ad hoc runtime support. Instead, the first call bootstraps by invoking runtime logic that efficiently selects a target method, and subsequent calls typically invoke the target method without having to re-bootstrap.

invokedynamic also benefits dynamic language implementers by supporting dynamically changing call site targets -- a call site, more specifically, a dynamic call site is an invokedynamic instruction. Furthermore, because the JVM internally supports invokedynamic, this instruction can be better optimized by the JIT compiler.

1.1 Method handles

The following code describes a method handle demonstration program consisting of main() and hello() class methods. This program's goal is to invoke hello() via a method handle.

Copy
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MHD
{
   public static void main(String[] args) throws Throwable
   {
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      MethodHandle mh = lookup.findStatic(MHD.class, "hello",
                                          MethodType.methodType(void.class));
      mh.invokeExact();
   }

   static void hello()
   {
      System.out.println("hello");
   }
}

Next code declares HW (Hello, World) and MHD classes. HW declares a public hello1() instance method and a private hello2() instance method. MHD declares a main() method that will attempt to invoke these methods.

Copy
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

class HW
{
   public void hello1()
   {
      System.out.println("hello from hello1");
   }

   private void hello2()
   {
      System.out.println("hello from hello2");
   }
}

public class MHD
{
   public static void main(String[] args) throws Throwable
   {
      HW hw = new HW();
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      MethodHandle mh = lookup.findVirtual(HW.class, "hello1",
                                           MethodType.methodType(void.class));
      mh.invoke(hw);
      mh = lookup.findVirtual(HW.class, "hello2",
                              MethodType.methodType(void.class));
   }
}

Next code introduces a Point class with a pair of 32-bit integer instance fields named x and y. Each field's setter and getter is accessed by calling MethodHandles.Lookup's findSetter() and findGetter() methods, and the resulting MethodHandle is returned. Each of findSetter() and findGetter() requires a Class argument that identifies the field's class, the field's name, and a Class object that identifies the field's signature.

Copy
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

class Point
{
   int x;
   int y;
}

public class MHD
{
   public static void main(String[] args) throws Throwable
   {
      MethodHandles.Lookup lookup = MethodHandles.lookup();

      Point point = new Point();

      // Set the x and y fields.

      MethodHandle mh = lookup.findSetter(Point.class, "x", 
                                          int.class);
      mh.invoke(point, 15);

      mh = lookup.findSetter(Point.class, "y", int.class);
      mh.invoke(point, 30);

      mh = lookup.findGetter(Point.class, "x", int.class);
      int x = (int) mh.invoke(point);
      System.out.printf("x = %d%n", x);

      mh = lookup.findGetter(Point.class, "y", int.class);
      int y = (int) mh.invoke(point);
      System.out.printf("y = %d%n", y);
   }
}

The following example is based on the Java Math class's double pow(double a, double b) class method. In this example, we obtain a method handle to the pow() method, and transform this method handle so that the second argument passed to pow() is always 10.

Example uses a combinator (a method that combines or transforms a pre-existing method handle into a new method handle) to modify the method handle to the pow() method. In this case, the combinator is MethodHandles's MethodHandle insertArguments(MethodHandle target, int pos, Object... values) method, which inserts a bound argument (10) into the pre-existing method handle. When invoke() (or invokeExact()) is subsequently called on the new method handle, only a single argument is required.

Copy
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MHD
{
   public static void main(String[] args) throws Throwable
   {
      MethodHandles.Lookup lookup = MethodHandles.lookup();
      MethodHandle mh = lookup.findStatic(Math.class, "pow",
                                          MethodType.methodType(double.class,
                                                                double.class,
                                                                double.class));
      mh = MethodHandles.insertArguments(mh, 1, 10);
      System.out.printf("2^10 = %d%n", (int) (double) mh.invoke(2.0));
   }
}

1.2 Bootstrapping

TO DO

This section is incomplete and will be concluded as soon as possible.

2 Dynalink (JEP 276)

Dynalink is an invokedynamic-based high-level linking and metaobject protocol library. It enables creation of languages on the JVM that can easily interoperate with plain Java objects and each other.

It lets you write your language runtime in a way where you can think in higher-level abstractions than the invokedynamic bytecode instruction.

Dynalink API provides a facility for linking high-level operations on objects such as "read a property", "write a property", "invoke a callable object", etc., expressed as names in INVOKEDYNAMIC call sites. Dynalink also provides a default linker for the usual semantics of these operations on plain Java objects (Java POJO "beans" linker), as well as a facility for installing your own language-specific linkers.

Dynalink has been used by Nashorn JavaScript engine internally starting from JDK 8. But, Dynalink is exposed as a public API starting from JDK 9. You can use dynalink to simplify invokedynamic generation when compiling your language for the Java platform.

Read about Dynalink motivation in Dynalink API specification.

3 JDK 9 Dynalink

Module jdk.dynalink