How Will Java Lambda Functions Be Compiled

How will Java lambda functions be compiled?

The VM decides how to implement lambda, not a compiler.

See Translation strategy section in Translation of Lambda Expressions.

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.

for construction from your example is most effective way in terms of simple compiling or perfomance (but the performance differences are very small, by the tests).

Addon:

I created and disassemble two examples:

for (String string: Arrays.asList("hello")) {
System.out.println(string);
}

Disassembled bytecode, constants and other information:

Classfile LambdaCode.class
Last modified 30.05.2013; size 771 bytes
MD5 checksum 79bf2821b5a14485934e5cebb60c99d6
Compiled from "LambdaCode.java"
public class test.lambda.LambdaCode
SourceFile: "LambdaCode.java"
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#22 // java/lang/Object."<init>":()V
#2 = Class #23 // java/lang/String
#3 = String #24 // hello
#4 = Methodref #25.#26 // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
#5 = InterfaceMethodref #27.#28 // java/util/List.iterator:()Ljava/util/Iterator;
#6 = InterfaceMethodref #29.#30 // java/util/Iterator.hasNext:()Z
#7 = InterfaceMethodref #29.#31 // java/util/Iterator.next:()Ljava/lang/Object;
#8 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Methodref #34.#35 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = Class #36 // test/lambda/LambdaCode
#11 = Class #37 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 StackMapTable
#19 = Class #38 // java/util/Iterator
#20 = Utf8 SourceFile
#21 = Utf8 LambdaCode.java
#22 = NameAndType #12:#13 // "<init>":()V
#23 = Utf8 java/lang/String
#24 = Utf8 hello
#25 = Class #39 // java/util/Arrays
#26 = NameAndType #40:#41 // asList:([Ljava/lang/Object;)Ljava/util/List;
#27 = Class #42 // java/util/List
#28 = NameAndType #43:#44 // iterator:()Ljava/util/Iterator;
#29 = Class #38 // java/util/Iterator
#30 = NameAndType #45:#46 // hasNext:()Z
#31 = NameAndType #47:#48 // next:()Ljava/lang/Object;
#32 = Class #49 // java/lang/System
#33 = NameAndType #50:#51 // out:Ljava/io/PrintStream;
#34 = Class #52 // java/io/PrintStream
#35 = NameAndType #53:#54 // println:(Ljava/lang/String;)V
#36 = Utf8 test/lambda/LambdaCode
#37 = Utf8 java/lang/Object
#38 = Utf8 java/util/Iterator
#39 = Utf8 java/util/Arrays
#40 = Utf8 asList
#41 = Utf8 ([Ljava/lang/Object;)Ljava/util/List;
#42 = Utf8 java/util/List
#43 = Utf8 iterator
#44 = Utf8 ()Ljava/util/Iterator;
#45 = Utf8 hasNext
#46 = Utf8 ()Z
#47 = Utf8 next
#48 = Utf8 ()Ljava/lang/Object;
#49 = Utf8 java/lang/System
#50 = Utf8 out
#51 = Utf8 Ljava/io/PrintStream;
#52 = Utf8 java/io/PrintStream
#53 = Utf8 println
#54 = Utf8 (Ljava/lang/String;)V
{
public test.lambda.LambdaCode();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 15: 0

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: iconst_1
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String hello
8: aastore
9: invokestatic #4 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
12: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
17: astore_1
18: aload_1
19: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
24: ifeq 47
27: aload_1
28: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
33: checkcast #2 // class java/lang/String
36: astore_2
37: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_2
41: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: goto 18
47: return
LineNumberTable:
line 35: 0
line 36: 37
line 37: 44
line 38: 47
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 18
locals = [ class java/util/Iterator ]
frame_type = 250 /* chop */
offset_delta = 28

}

and

Arrays.asList("hello").forEach(p -> {System.out.println(p);});

Disassembled bytecode, constants and other information:

Classfile LambdaCode.class
Last modified 30.05.2013; size 1262 bytes
MD5 checksum 4804e0a37b73141d5791cc39d51d649c
Compiled from "LambdaCode.java"
public class test.lambda.LambdaCode
SourceFile: "LambdaCode.java"
InnerClasses:
public static final #64= #63 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#28 invokeinterface java/util/function/Consumer.accept:(Ljava/lang/Object;)V
#29 invokestatic test/lambda/LambdaCode.lambda$0:(Ljava/lang/String;)V
#30 (Ljava/lang/String;)V
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // java/lang/String
#3 = String #23 // hello
#4 = Methodref #24.#25 // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
#5 = InvokeDynamic #0:#31 // #0:lambda$:()Ljava/util/function/Consumer;
#6 = InterfaceMethodref #32.#33 // java/util/List.forEach:(Ljava/util/function/Consumer;)V
#7 = Fieldref #34.#35 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #36.#37 // java/io/PrintStream.println:(Ljava/lang/String;)V
#9 = Class #38 // test/lambda/LambdaCode
#10 = Class #39 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 lambda$0
#18 = Utf8 (Ljava/lang/String;)V
#19 = Utf8 SourceFile
#20 = Utf8 LambdaCode.java
#21 = NameAndType #11:#12 // "<init>":()V
#22 = Utf8 java/lang/String
#23 = Utf8 hello
#24 = Class #40 // java/util/Arrays
#25 = NameAndType #41:#42 // asList:([Ljava/lang/Object;)Ljava/util/List;
#26 = Utf8 BootstrapMethods
#27 = MethodHandle #6:#43 // invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#28 = MethodHandle #9:#44 // invokeinterface java/util/function/Consumer.accept:(Ljava/lang/Object;)V
#29 = MethodHandle #6:#45 // invokestatic test/lambda/LambdaCode.lambda$0:(Ljava/lang/String;)V
#30 = MethodType #18 // (Ljava/lang/String;)V
#31 = NameAndType #46:#47 // lambda$:()Ljava/util/function/Consumer;
#32 = Class #48 // java/util/List
#33 = NameAndType #49:#50 // forEach:(Ljava/util/function/Consumer;)V
#34 = Class #51 // java/lang/System
#35 = NameAndType #52:#53 // out:Ljava/io/PrintStream;
#36 = Class #54 // java/io/PrintStream
#37 = NameAndType #55:#18 // println:(Ljava/lang/String;)V
#38 = Utf8 test/lambda/LambdaCode
#39 = Utf8 java/lang/Object
#40 = Utf8 java/util/Arrays
#41 = Utf8 asList
#42 = Utf8 ([Ljava/lang/Object;)Ljava/util/List;
#43 = Methodref #56.#57 // java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#44 = InterfaceMethodref #58.#59 // java/util/function/Consumer.accept:(Ljava/lang/Object;)V
#45 = Methodref #9.#60 // test/lambda/LambdaCode.lambda$0:(Ljava/lang/String;)V
#46 = Utf8 lambda$
#47 = Utf8 ()Ljava/util/function/Consumer;
#48 = Utf8 java/util/List
#49 = Utf8 forEach
#50 = Utf8 (Ljava/util/function/Consumer;)V
#51 = Utf8 java/lang/System
#52 = Utf8 out
#53 = Utf8 Ljava/io/PrintStream;
#54 = Utf8 java/io/PrintStream
#55 = Utf8 println
#56 = Class #61 // java/lang/invoke/LambdaMetafactory
#57 = NameAndType #62:#66 // metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#58 = Class #67 // java/util/function/Consumer
#59 = NameAndType #68:#69 // accept:(Ljava/lang/Object;)V
#60 = NameAndType #17:#18 // lambda$0:(Ljava/lang/String;)V
#61 = Utf8 java/lang/invoke/LambdaMetafactory
#62 = Utf8 metaFactory
#63 = Class #71 // java/lang/invoke/MethodHandles$Lookup
#64 = Utf8 Lookup
#65 = Utf8 InnerClasses
#66 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#67 = Utf8 java/util/function/Consumer
#68 = Utf8 accept
#69 = Utf8 (Ljava/lang/Object;)V
#70 = Class #72 // java/lang/invoke/MethodHandles
#71 = Utf8 java/lang/invoke/MethodHandles$Lookup
#72 = Utf8 java/lang/invoke/MethodHandles
{
public test.lambda.LambdaCode();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 15: 0

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=1, args_size=1
0: iconst_1
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String hello
8: aastore
9: invokestatic #4 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
12: invokedynamic #5, 0 // InvokeDynamic #0:lambda$:()Ljava/util/function/Consumer;
17: invokeinterface #6, 2 // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
22: return
LineNumberTable:
line 28: 0
line 38: 22
}

Compiler generated class-file is more complicated and larger (771b vs 1262b) for Lambda example.

What is a Java 8 Lambda Expression Compiled to?

The current draft of the Java 8 Language Specification states (chapter 15.27.4)

The value of a lambda expression is a reference to an instance of a
class with the following properties:

  • The class implements the targeted functional interface and, if the target type is an intersection type, every other interface element of the intersection.
  • The class declares a method that overrides the functional interface supertype's abstract methods (and, potentially, some other methods of its superinterfaces).
  • The method's parameter types, return type, and thrown types are given by the interface's function type.
  • The method's body has the effect of evaluating the lambda body, if it is an expression, or of executing the lambda body, if it is a
    block; if a result is expected, it is returned from the method.
  • The class overrides no other methods of the interface or interfaces mentioned above, except that it may override methods of the Object
    class.

Note that the JLS doesn't say anything about how the code should be compiled except that the byte code should support the specification above.

As such, the object returned by the lambda expression

x -> System.out.print(x);  

will be an instance of a class that follows the above rules.

Given your comment that

consumer.getClass()

returns the following class

Example$$Lambda$1/1072591677

it seems that it is generating a proxy-like class specific for lambda expressions.

See here:

  • How will Java lambda functions be compiled?

Are Java 8 lambdas compiled as inner classes, methods or something else?

The expression itself, assuming you pass an actual lambda expression and not a method reference, is compiled as a separate, synthetic method. In addition to any formal arguments to the expected functional interface (e.g., a single String in the case of Consumer<String>), it will include arguments for any captured values.

At the code location where a lambda expression or method reference appears, an invokedynamic instruction is emitted. The first time this instruction is hit, a call is made into a bootstrap method on LambdaMetafactory. This bootstrap method will fix up an actual implementation of the target functional interface which delegates to the target method, and this is what gets returned. The target method is either the synthetic method representing the lambda body or whichever named method was provided using the :: operator. While a class that implements the functional interface is being created, the process is deferred; it does not happen at compile time.

Finally, the runtime patches the invokedynamic site with the bootstrap result1, which is effectively a constructor call to the generated delegate with any captured values passed in, including (possibly) an invocation target2. This alleviates the performance hit by removing the bootstrapping process for subsequent calls.


1 See java.lang.invoke end of chapter "timing of linkage", courtesy of @Holger.

2 In the case of a lambda which no captures, the invokedynamic instruction will usually resolve to a shared delegate instance that can be reused during subsequent calls, though this is an implementation detail.

Can HotSpot inline lambda function calls?

Your lambda expression is compiled into an ordinary method, while the JRE will generate a class fulfilling the functional interface and calling that method. In current HotSpot versions, this generated class works almost like an ordinary class, the main differences are that it may invoke private target methods and that it is not back-referenced by a ClassLoader.

None of these properties hinders optimizations, in the end, you only have a chain of ordinary method invocations. The biggest obstacle with such code with the current JVMs are inlining limits, regarding the maximum depth (defaults to nine nested methods IIRC) and maximum resulting code size. Some of these defaults are very old and were not revised since their last definition. However, such limits may affect very long stream pipelines, not a use case like your plain forEach.

So the general answer is that HotSpot is capable of performing such optimizations, but like with all optimizations, it will let your code run a few times, before determining whether it is performance critical and perform the optimization(s), if so.

Where's the code for a lambda located in a java class file?

If you want to see the code of your lambda body a*a+2*a+1, you should call javap -c -v -p t to see also the private methods:

private static java.lang.Integer lambda$main$0(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokevirtual #7 // Method java/lang/Integer.intValue:()I
4: aload_0
5: invokevirtual #7 // Method java/lang/Integer.intValue:()I
8: imul
9: iconst_2
10: aload_0
11: invokevirtual #7 // Method java/lang/Integer.intValue:()I
14: imul
15: iadd
16: iconst_1
17: iadd
18: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
21: areturn
LineNumberTable:
line 4: 0

More detailed answer about the lambda inner implementation is here: How will Java lambda functions be compiled?

Java Lambdas : How it works in JVM & is it OOP?

I wouldn't waste my time thinking whether the lambda expressions are a violation of OO principles. Its goal is to increase the power of a language and not to write an OO code, I don't see how lambdas can violate encapsulation, inheritance or polymorphism.

This article explains how Java handles lambda expressions:

What’s interesting about Lambda expressions is that from the JVM’s perspective they’re completely invisible. It has no notion of what an anonymous function or a Lambda expression is. It only knows bytecode which is a strict OO specification. It’s up to the makers of the language and its compiler to work within these constraints to create newer, more advanced language elements.

Considering the following code:

List names = Arrays.asList("1", "2", "3");
Stream lengths = names.stream().map(name -> name.length());

... It begins quite simply by loading the names var and invokes its .stream() method, but then it does something quite elegant. Instead of creating a new object that will wrap the Lambda function, it uses the new invokeDynamic instruction which was added in Java 7 to dynamically link this call site to the actual Lambda function.

aload_1 //load the names var

// call its stream() func
invokeinterface java/util/List.stream:()Ljava/util/stream/Stream;

//invokeDynamic magic!
invokedynamic #0:apply:()Ljava/util/function/Function;

//call the map() func
invokeinterface java/util/stream/Stream.map:
(Ljava/util/function/Function;)Ljava/util/stream/Stream;

InvokeDynamic is an instruction that was added in Java 7 to make the JVM less strict, and allows dynamic languages to bind symbols at run-time, vs. doing all the linkage statically when the code is compiled by the JVM.

The Lambda code

aload_0
invokevirtual java/lang/String.length:()
invokestatic java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
areturn

How to transform a java application compiled with java13 to an aws lambda function

The simplest solution, assuming that you have not used language features or library methods that are not supported by Java 11, is to use the -source and -target options to tell the compiler to produce Java 11 compatible output. You don't say what build tool you're using, but it should provide some way to configure this (for example, Maven).

If you are using Java-13-specific features, I would make an effort to remove them, so that you can target Java 11.

But if that isn't possible, then your only option is to deploy your Lambda using a Lambda container image. You won't be able to use the provided base images, so you'll need to learn how to build Docker images. And you'll need to explicitly build the Lambda runtime API bootstrap for your image.

Is there any runtime benefit of using lambda expression in Java?

lambdas do NOT create a new scope, they share the same scope as the enclosing block/ environment” is an almost correct statement (they do create a new scope, but not the way inner classes do), but doesn’t have anything to do with runtime performance. This has to do with correctness of the code.

Within an anonymous inner class, identifiers may get resolved through the lexical scope, finding a match in the surrounding scope, or by inheritance, finding a match in the class hierarchy of the anonymous inner class. The rules for resolving identifiers in this scenario are complex and easy to confuse.

Further, the body of an anonymous class creates a new scope that allows to create variables having the same name as local variables of the surrounding context, shadowing these variables.

In contrast, a lambda expression works like other expressions within the context they are written in. They do not inherit any members from the functional interface they will get converted to, they can not create new variables shadowing existing local variables and even this and super have the same meaning as within the surrounding context:

JLS§15.27.2. Lambda Body

Unlike code appearing in anonymous class declarations, the meaning of names and the this and super keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).

So when you have the expressions x.foo(y) and () -> x.foo(y) within the same block, it will be obvious whether x and y will be the same x and y for both expressions and hence, it will be the same foo method in each case, which you can not say that simple for anonymous inner classes, as you have to analyze the entire inner class and its type hierarchy first.

This makes lambda expressions ideal for scenarios where you want to define a local function and, e.g. pass it to a method as a parameter, without even thinking about the actual interface being used. The interface itself does not influence the lambda expression beyond defining the functional signature.

But this also implies that there might be use cases of anonymous classes that can’t be covered by lambda expressions. But the purpose of lambda expressions isn’t to be a general replacement for anonymous inner classes.


When it comes to performance or easy of parallel processing, shmosel’s answer says it already. We can’t make such general statements without knowing which operation/problem we are looking at and which solutions we are actually comparing.



Related Topics



Leave a reply



Submit