74

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. They are translated to a static method with well defined arguments. And this method can be invoked using invokestatic.

So then what is the need of invokedynamic for lambdas, especially when there is a performance hit?

Dzamo Norton
  • 1,194
  • 11
  • 17
Kshitiz Sharma
  • 17,947
  • 26
  • 98
  • 169
  • 8
    Brian Goetz explains it here: http://wiki.jvmlangsummit.com/images/1/1e/2011_Goetz_Lambda.pdf – JB Nizet May 02 '15 at 12:39
  • 7
    `invokedynamic` is **not** used **to invoke** lambda, but to create and bind a MethodHandle to lambda's body at the given call site. – apangin May 02 '15 at 12:45
  • 10
    Indy is used to _capture_ the lambda, not invoke it. – Brian Goetz May 02 '15 at 14:10
  • 9
    We use `invokedynamic` because it provides a performance _improvement_, not a performance hit, over the "obvious" translation schemes. (I don't know where you got the idea that there would be a performance hit, but this is incorrect.) – Brian Goetz May 03 '15 at 14:25
  • 4
    There is performance test at http://stackoverflow.com/questions/19001241/big-execution-time-difference-between-java-lambda-vs-anonymous-class: "The bottom line is that post JIT compilation, lambdas and anonymous classes perform similarly on current Hostpot JVM implementations". – Vadzim Jul 30 '15 at 15:24
  • lambdas are not translated to static methods. – mjjaniec May 24 '16 at 09:50
  • @mjjaniec Reference? – Kshitiz Sharma May 24 '16 at 10:01
  • @BrianGoetz are you saying that Invoke Dynamic is faster than just calling anonymous classes normally? That would be surprising. If not then what are you saying -- that Invoke Dynamic helps with JavaScript? But that is not the question. Why use them for Lambdas? Because they were there? Some never qualified "flexibility"? – Tuntable Aug 03 '20 at 00:27

3 Answers3

75

Lambdas are not invoked using invokedynamic, their object representation is created using invokedynamic, the actual invocation is a regular invokevirtual or invokeinterface.

For example:

// creates an instance of (a subclass of) Consumer 
// with invokedynamic to java.lang.invoke.LambdaMetafactory 
something(x -> System.out.println(x));   

void something(Consumer<String> consumer) {
      // invokeinterface
      consumer.accept("hello"); 
}

Any lambda has to become an instance of some base class or interface. That instance will sometimes contain a copy of the variables captured from the original method and sometimes a pointer to the parent object. This can be implemented as an anonymous class.

Why invokedynamic

The short answer is: to generate code in runtime.

The Java maintainers chose to generate the implementation class in runtime. This is done by calling java.lang.invoke.LambdaMetafactory.metafactory. Since the arguments for that call (return type, interface, and captured parameters) can change, this requires invokedynamic.

Using invokedynamic to construct the anonymous class in runtime, allows the JVM to generate that class bytecode in runtime. The subsequent calls to the same statement use a cached version. The other reason to use invokedynamic is to be able to change the implementation strategy in the future without having to change already compiled code.

The road not taken

The other option would be the compiler creating an innerclass for each lambda instantiation, equivalent to translating the above code into:

something(new Consumer() { 
    public void accept(x) {
       // call to a generated method in the base class
       ImplementingClass.this.lambda$1(x);

       // or repeating the code (awful as it would require generating accesors):
       System.out.println(x);
    }
);   

This requires creating classes in compile time and having to load then during runtime. The way jvm works those classes would reside in the same directory as the original class. And the first time you execute the statement that uses that lambda, that anonymous class would have to be loaded and initialized.

About performance

The first call to invokedynamic will trigger the anonymous class generation. Then the opcode invokedynamic is replaced with code that's equivalent in performance to the writing manually the anonymous instantiation.

Lii
  • 11,553
  • 8
  • 64
  • 88
Daniel Sperry
  • 4,381
  • 4
  • 31
  • 41
  • 1
    What backs your claim that “repeated calls to `invokedynamic` will generate more garbage …” or that repeated execution of the same `invokedynamic` instruction creates *any* garbage? – Holger May 04 '15 at 08:24
  • 1
    It seems you did not understand what you saw. There are a lot of invocations caused by the initialization of the `java.lang.invoke` framework, which are not related to your lambda expression at all. Step over until `i==1` and see how many times your breakpoint will be hit *then*. – Holger May 04 '15 at 11:25
  • Indeed. Do you know what happens to the callsite? I want to make sure the invokedynamic is not being replaced with something that creates new Object[] arrays before reverting my answer. And `MethodHandle.invokeExact` does take an Object[] as parameter. – Daniel Sperry May 04 '15 at 11:46
  • 3
    Please, don’t confuse the first-time overhead of the framework with the overhead of a single `invokedynamic` instruction. If you place one lambda expression *before* the loop and check then, you will see that the `invokedynamic` instruction within the loop will call `findStatic` exactly one time during linkage on the first invocation. This applies to all other subsequently executed `invokedynamic` instructions as well, and it’s *specified* that after one completed linkage of a dynamic callsite, it will stay linked forever. And method handles do *not* take `Object[]` as parameter. – Holger May 04 '15 at 11:50
  • I'm not. I promise. And `invokeExact` does take an Object[] as parameter. – Daniel Sperry May 04 '15 at 11:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76882/discussion-between-daniel-sperry-and-holger). – Daniel Sperry May 04 '15 at 11:54
  • 2
    See [the package documentation of `java.lang.invoke`](http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/package-summary.html#indyinsn): *Every dynamic call site transitions at most once from unlinked to linked, just before its first invocation. There is no way to undo the effect of a completed bootstrap method call.* And try to understand [Signature polymorphism](http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandle.html#sigpoly) before discussing any further… – Holger May 04 '15 at 11:55
  • Thanks for the clarification. The references are clear about method handle being capable of taking arguments directly from the stack without boxing. I'll edit that part of my answers. – Daniel Sperry May 04 '15 at 12:14
  • So the invoke dynamic is used because it is more flexible. It would be good to have an example of where this flexibility makes a difference. Certainly the approach wins many points for complexity. – Tuntable Sep 05 '16 at 07:40
  • @Tuntable where it gains "flexibility" is in the fact that should the Oracle engineers decide to _alter_ this mechanism, old code will still work without changes. To quote a provided answer: "_there are two competing goals: maximizing flexibility for future optimization by not committing to a specific strategy, vs providing stability in the classfile representation. We can achieve both of these goals by using the invokedynamic feature from JSR 292 to separate the binary representation of lambda creation in the bytecode from the mechanics of evaluating the lambda expression at runtime_" – Martin Marconcini Jul 31 '20 at 12:01
51

Brain Goetz explained the reasons for the lambda translation strategy in one of his papers which unfortunately now seem unavailable. Fortunately I kept a copy:

Translation strategy

There are a number of ways we might represent a lambda expression in bytecode, such as inner classes, method handles, dynamic proxies, and others. Each of these approaches has pros and cons. In selecting a strategy, there are two competing goals: maximizing flexibility for future optimization by not committing to a specific strategy, vs providing stability in the classfile representation. We can achieve both of these goals by using the invokedynamic feature from JSR 292 to separate the binary representation of lambda creation in the bytecode from the mechanics of evaluating the lambda expression at runtime. Instead of generating bytecode to create the object that implements the lambda expression (such as calling a constructor for an inner class), we describe a recipe for constructing the lambda, and delegate the actual construction to the language runtime. That recipe is encoded in the static and dynamic argument lists of an invokedynamic instruction.

The use of invokedynamic lets us defer the selection of a translation strategy until run time. The runtime implementation is free to select a strategy dynamically to evaluate the lambda expression. The runtime implementation choice is hidden behind a standardized (i.e., part of the platform specification) API for lambda construction, so that the static compiler can emit calls to this API, and JRE implementations can choose their preferred implementation strategy. The invokedynamic mechanics allow this to be done without the performance costs that this late binding approach might otherwise impose.

When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression, possibly with some additional arguments (for values captured from the lexical scope, if any.) At the point at which the lambda expression would be captured, it generates an invokedynamic call site, which, when invoked, returns an instance of the functional interface to which the lambda is being converted. This call site is called the lambda factory for a given lambda. The dynamic arguments to the lambda factory are the values captured from the lexical scope. The bootstrap method of the lambda factory is a standardized method in the Java language runtime library, called the lambda metafactory. The static bootstrap arguments capture information known about the lambda at compile time (the functional interface to which it will be converted, a method handle for the desugared lambda body, information about whether the SAM type is serializable, etc.)

Method references are treated the same way as lambda expressions, except that most method references do not need to be desugared into a new method; we can simply load a constant method handle for the referenced method and pass that to the metafactory.

So, the idea here seemed to be to encapsulate the translation strategy and not commit to a particular way of doing things by hiding those details. In the future when type erasure and lack of value types have been solved and maybe Java supports actual function types, they might just as well go there and change that strategy for another one without causing any problems in the users' code.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
7

Current Java 8's lambda implementation is a compound decision:

    1. Compile the lambda expression to a static method in the enclosing class; instead of compiling lambdas to separate inner class files (Scala compiles this way, which generates many $$$ class files)
    1. Introduce a constant pool: BootstrapMethods, which wraps the static method invocation to callsite object (can be cached for later use)

So to answer your question,

    1. the current lambda implementation using invokedynamic is a little bit faster than the separate inner class way, because no need to load these inner class files, but instead create the inner class byte[] on the fly (to satisfy for example the Function interface), and cached for later use.
    1. JVM team may still choose to generate separate inner class (by referencing the enclosing class's static methods) files: it's flexible
Wild Pottok
  • 318
  • 3
  • 8
fjolt
  • 341
  • 3
  • 3