What's invokedynamic and how do I use it?
Asked Answered
G

5

189

I keep hearing about all the new cool features that are being added to the JVM and one of those cool features is invokedynamic. I would like to know what it is and how does it make reflective programming in Java easier or better?

Gymnast answered 10/7, 2011 at 2:15 Comment(0)
S
185

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.

Selfcommand answered 10/7, 2011 at 2:23 Comment(7)
In day to day Java programming it is not uncommon to see reflection being used to invoke methods dynamically with meth.invoke(args). So how does invokedynamic fit with meth.invoke?Gymnast
The blog post I mention talks about MethodHandle, which is really the same kind of thing but with much more flexibility. But the real power in all this comes not in additions to the Java language, but in the capabilities of the JVM itself in supporting other languages that are intrinsically more dynamic.Selfcommand
+1 for the "really sweet blog post that gives a lot of detail".Ev
@ErnestFriedman-Hill Can you define "looser specification" please? Does it mean 1)the arguments do not have to match exactly? 2)Some of the arguments can be missing?Carroty
Seems that Java 8 translates some of the lambdas using invokedynamic which makes it performant (compared to wrap them in an anonymous inner-class which was almost the only choice before introducing invokedynamic). Most probably a lot of functional programming languages on top of JVM will opt to compile to this instead of anon-inner-classes.Justness
Just a small warning, that blog post from 2008 is hopelessly outdated and doesn’t reflect the actual release state (2011).Sculpture
FYI, Java 9 uses invokedynamic as part of implementing JEP 280: Indify String Concatenation.Aphra
G
21

As part of my Java Records article, I articulated about the motivation behind Invoke Dynamic. Let's start with a rough definition of Indy.

Introducing 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!

User-Definable Bytecode

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.

How Does Indy Work?

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:

enter image description here

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.

Example: Java 14 Records

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:

  • An instance of MethodHandles.Lookup representing the lookup context (The Ljava/lang/invoke/MethodHandles$Lookup part).
  • The method name (i.e. 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.
  • The TypeDescriptor for the method (Ljava/lang/invoke/TypeDescriptor part).
  • A type token, i.e. Class<?>, representing the Record class type. It’s Class<Range> in this case.
  • A semi-colon separated list of all component names, i.e. min;max.
  • One 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.

Why Indy?

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!

Other Examples

In addition to Java Records, the invoke dynamic has been used to implement features like:

Gazette answered 5/4, 2020 at 14:14 Comment(0)
C
8

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.

Consummation answered 10/7, 2011 at 4:59 Comment(10)
invokedynamic was never intended to be used for Java programmers. IMO it doesn't fit the Java philosophy at all. It was added as a JVM feature for non-Java languages.Geoff
@Mark: There is a way to take advantage of it. I remember seeing actual Java code making use of MethodHandle.Gymnast
@Mark Never intended by who? It's not like there's a clear power structure in Java language celebrities, or there's a well defined collective "intention". As for language philosophy - it's quite feasible, see Neal Gafter (traitor!) explanation: infoq.com/presentations/Statically-Dynamic-Typing-Neal-GafterConsummation
This doesn't compare to invokedynamic at all. The dynamic keyword in C# says something about methods are resolved, whereas invokedynamic says something about how methods are called.Granitite
You are right. But InvokeDynamic certainly would depend on invokedynamic, and it gives Java programmers an API to take advantage of invokedynamic easily. (sure, we can always generate bytecode on the fly in a pure Java program...)Consummation
@mark peters: invokedynamic is actually also intended for java programmers only not directly accessible. It's the basis for Java 8's closures.Granitite
@irreputable: Never intended by the JSR contributors. It's telling that the name of the JSR is "Supporting Dynamically Typed Languages on the Java Platform". Java isn't a dynamically typed language.Geoff
@M Platvoet: I haven't stayed up to date with closures, but it certainly wouldn't be an absolute requirement for closures. Another option they discussed was simply making closures syntactic sugar for anonymous inner classes which could be done without a VM spec change. But my point was that the JSR was never intended to bring dynamic typing to the Java language, that much is clear if you read the JSR.Geoff
@Mark in this old article, there's an example InvokeDynamic.hail(x), yet hail is not a statically declared method; it is manufactured at runtime. java.sun.com/developer/technicalArticles/DynTypeLangConsummation
One way to take advantage of them is translating lambdas to invokedynamic bytecode instead of wrapping the lambda in an anonymous inner class which is what Java 8 is doing (for some lambdas which cannot be translated with invokestatic which will be a static method)Justness
T
7

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.

Tamarisk answered 13/7, 2019 at 6:13 Comment(0)
A
0

The short answer is invokedynamic is a new opcode in the JVM that didn't exist prior to JAVA 7.

As far as reflection, within the context of this definition: Java Reflection is a process of examining or modifying the run time behavior of a class at run time., however, I believe more explanation is needed. From the article below:

For example, reflection predates both collections and generics. As a result, method signatures are represented by Class[] in the Reflection API. This can be cumbersome and error-prone, and it is hampered by the verbose nature of Java’s array syntax. It is further complicated by the need to manually box and unbox primitive types and to work around the possibility of void methods.

Method handles to the rescue

Instead of forcing the programmer to deal with these issues, Java 7 introduced a new API, called MethodHandles, to represent the necessary abstractions. The core of this API is the package java.lang.invoke and especially the class MethodHandle. Instances of this type provide the ability to call a method, and they are directly executable. They are dynamically typed according to their parameter and return types, which provides as much type safety as possible, given the dynamic way in which they are used. The API is needed for invokedynamic, but it can also be used alone, in which case it can be considered a modern, safe alternative to reflection.

Quoting from Understanding Java method invocation with invokedynamic

These four are the bytecode representations of the standard forms of method invocation used in Java 8 and Java 9, and they are invokevirtual, invokespecial, invokeinterface, and invokestatic.

This raises the question of how the fifth opcode, invokedynamic, enters the picture. The short answer is that, as of Java 9, there was no direct support for invokedynamic in the Java language.

In fact, when invokedynamic was added to the runtime in Java 7, the javac compiler would not emit the new bytecode under any circumstances whatsoever.

As of Java 8, invokedynamic is used as a primary implementation mechanism to provide advanced platform features. One of the clearest and simplest examples of this use of the opcode is in the implementation of lambda expressions.

So again, invokedynamic is a new opcode that allows for a new object reference type in JAVA, a Lambda.

Assurance answered 29/9, 2022 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.