Introduction

Java 7 introduced multiple features at the time of its release. However, one feature that stands out among Java developers is invokedynamic bytecode that was part of JSR 292. We will be further discussing in this article why it is such a powerful tool for the Java platform, especially for JVM dynamic languages.

What is invokedynamic Java?

Invokedynamic Java is a bytecode instruction that allows the implementation of dynamic languages in the JVM using dynamic methods. It allows the Java compiler to generate a code that can call methods with a certain specification. It can be used to build more flexible and more efficient JVM-based languages.

When invokedynamic Java was initially introduced in Java 7, it was intended to further enhance the JVM support for dynamically typed languages.  Since its first release, the invokedynamic opcode has been extensively used for dynamic JVM-based languages like JRuby and even for statically typed languages like Java itself.

Dynamic and static languages

A dynamically-typed language is a high-level programming language whose type checking is usually done at runtime. Type checking verifies that a program is correct: all operation arguments have the correct type. JavaScript and Groovy are two very common examples of dynamic languages.

On the contrary, a statically-typed language performs type checking at compile time. The compiler verifies that a program is correct. Java is an example of a static language. The Java compiler uses this information to produce a strongly-typed bytecode, which is then executed efficiently by the JVM.

Invoke bytecodes

There have been some other invoke bytecodes before invokedynamic but it has been the most prominent addition since Java 1.0. It joined the four pre-existing invoke bytecodes that are invokevirtual, invokestatic, invokeinterface and invokespecial. These four existing opcodes are used to implement all types of method dispatches that Java developers are usually familiar with, specifically:

  • Invokevirtual is used for the standard dispatch for instance methods.
  • invokestatic is used for the dispatch of static methods.
  • invokeinterface, for dispatching a method call via an interface.
  • invokespecial is required when non-virtual (i.e. “exact”) dispatch is needed.

How does invokedynamic Java facilitate dynamically typed languages?

In a dynamic or dynamically typed language, developers have to pass appropriate types otherwise they can face runtime failures.  Another challenge is that dynamic languages usually offer features to add or remove fields or methods from existing classes. Due to this, it is required to defer class, method and field resolution to runtime.

These challenges then traditionally required ad hoc runtime support to be built on top of the JVM. Due to runtime support, the generated bytecode often needed several actual JVM method invocations for a single dynamic language method invocation. It was not an ideal solution as it significantly affects the performance of the program. The different execution paths also made it impossible for the just-in-time (JIT) compiler in JVM to optimize the code.

Invokedynamic Java addresses these performance issues by removing the ad hoc runtime support, in place of that it first calls the bootstrap method. The bootstrap method is a simple piece of Java code that is produced to set up the invocation process. Therefore, it can contain any logic. It invokes the runtime logic that efficiently selects a target method, and subsequently calls the target method without having to re-bootstrap.

Invokedynamic Java also facilitates dynamic language implementers by supporting dynamically changing call site targets. A call site is an invokedynamic instruction. As JVM internally supports Java invokedynamic, this instruction can be significantly optimized by the JIT compiler.

What is a call site?

A Call site is a holding position for a MethodHandle, which is called its target. An invokedynamic instruction linked to a call site delegates all calls to the current target of the site. A call site may be associated with several invokedynamic instructions, or it can be free-floating, associated with none of them.

Constant call site

Once the JVM sees invokedynamic for the first time, the bootstrap method is called. The bootstrap method then encapsulates the generated inner class inside a special type of call site called ConstantCallSite. As the name describes, this type of Call site can never be changed after it is set up. That is why, after the first setup, the JVM will always use the fast path to directly re-call the bootstrap method.

Despite being the most efficient type of call site in invokedynamic Java, it is not the only option available. Java also provides VolatileCallSite and MutableCallSite but it is the most commonly used.

What are method handles?

For Java invokedynamic to work properly, a key concept is the method handle. It is required to represent the method that is supposed to be called from the invokedynamic call site. Each invokedynamic instruction is associated with a bootstrap method. When the invokedynamic instruction is reached by the interpreter, the bootstrap method is called. It then returns an object containing a method handle that indicates which method the call site should execute.

The invokedynamic uses method handles via the bootstrap methods mechanism as mentioned before. When a class containing invokedynamic is loaded, the call sites are in an unlaced state, and when the bootstrap method returns, the targeted Call site and method handle are now laced into the call site.

The signature for a bootstrap method is mentioned below:

static Call site bootstrap(MethodHandles.Lookup caller, String name, MethodType type);

It is very similar to reflection, but as reflection has some limitations, It is not that feasible to be used with Java invokedynamic. that’s why java.lang.invoke.MethodHandle was introduced in the Java 7 API, to represent methods that invokedynamic Java can target. The class MethodHandle also receives some special treatment from the JVM to operate correctly.

Other applications of invokedynamic Java

Other than facilitating dynamic languages, invokedynamic Java has some more useful applications in Java.

Records in Java

Records were introduced as a preview feature in Java 14. Their primary purpose was to provide a better syntax to declare classes that are supposed to be just data holders.

See this simple record example below:

public record Fruit(String name, int code) {}

Java compiler will now generate an appropriate implementations for accessor methods, toString, equals, and hashcode.

To implement toString, equals, or hashcode, Java will be using invokedynamic. For example, the bytecode for equals is mentioned below:

public final boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokedynamic #27,  0  // InvokeDynamic #0:equals:(LColor;Ljava/lang/Object;)Z
       7: ireturn

The alternate solution without using invokedynamics would be to find all record fields and generate the equals logic based on those fields at compile-time. The code can get lengthy in that way depending on the number of fields. To cater to that, Java calls a bootstrap method to link the implementation at runtime. In return, the bytecode length will now remain constant regardless of the number of fields.

String concatenation

Before invokedynamic, string concatenations use to be implemented using StringBuilder. String concatenation is now also using invokedynamic. For example, to concatenate a constant string with a simple variable:

"abc" + ThreadLocalRandom.current().nextInt();

This is how the bytecode for this one line looks like:

0: invokestatic  #7          // Method ThreadLocalRandom.current:()LThreadLocalRandom;

3: invokevirtual #13         // Method ThreadLocalRandom.nextInt:()I

6: invokedynamic #17,  0     // InvokeDynamic #0:makeConcatWithConstants:(I)LString;

The bootstrap methods used for string concatenations are present in the StringConcatFactory class.

Conclusion

This was  a brief introduction to Invokedynamic Java and all you needed to know about the call site, bootstrap methods and method handlers. We also covered some useful applications of invokedynamic Java. It goes without saying that there is  a lot more to be discovered about Invokedynamic Java.

See Also: What Are Advantages Of Using Inner Class In Java?

Invokedynamic Java is often referred to as the secret weapon of Java because it not only facilitates dynamic languages but also significantly improves the internal working of a Java code by integrating with several other applications.

Author

Shaharyar Lalani is a developer with a strong interest in business analysis, project management, and UX design. He writes and teaches extensively on themes current in the world of web and app development, especially in Java technology.

Write A Comment