Why are Java 8 lambdas invoked using invokedynamic?

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.

Leave a Comment