Part 4 – Lambda Expression Internal Working

How Lambda Expression Internally Works

What have we learned so far
1. Lambda Expression in Java 8
PART 1 – LAMBDA EXPRESSION IN JAVA 8

2. Lambda vs Anonymous class in Java 8
PART 2 – LAMBDA VS ANONYMOUS CLASS IN JAVA 8

3. Part 3– final and effectively final variables in Lambda Java 8
PART 3– FINAL AND EFFECTIVELY FINAL VARIABLES IN LAMBDA JAVA 8

How Lambda internally works?

Lambda Expression Internal Working

No Anonymous Class generation during compile time

Lambda Expressions are not completely Syntactic Sugar, which means compiler doesn’t translate them into something which is already understood by JVM.
Similarly, for lambda, one straight forward approach could be:
Desugar the new syntax into anonymous classes during compile time means to replace the lambdas with some piece of code(something like Anonymous class). But conceptually that wouldn’t make them true functions. Also, anonymous classes have their own problems from day one, JVM has to perform rather an expensive process of initiation a new class instance whenever it encounters an inline anonymous class load, link and verify the anonymous class into memory. Anonymous classes are a bit hard to read and understand as they carry quite a difficult syntax with them. These are the reasons lambda designers didn’t go for the anonymous class approach. That also means by using lambda now, we can get rid of those anonymous class explosion in our output directory. For example, you must have noticed that in our previous part, creating an anonymous class inside LambdaVsAnonymousClass.java results in something like LambdaVsAnonymousClass$1.class.

What lambdas are compiled to?

Lambda syntax written by the developer is desugared into JVM level instructions generated during compilation. so the actual responsibility of constructing lambda is deferred to runtime.

To make the Java less compile time strict, and to allow JVM languages to bind code at run-time, Java 7 introduced a new invokedynamic bytecode instruction to the JVM and a new java.lang.invoke API package. invokedynamic facilitates the implementation of dynamic languages (for the JVM) through dynamic method invocation. Lambda engineers chose invokedynamic approach for lambdas. Let’s see what the Lambdas are compiled to :

Program : 
package com.onlyfullstack;

public class LambdaInternalWorking {

 public static void main(String[] args) {
  Runnable runnable = () -> System.out.println("Calling from Lambda");
 }
}
Let’s compile the below program:
javac LambdaInternalWorking.java

Let’s disassemble the .class file to see the contents:

javap -p LambdaInternalWorking.class
Compiled from "LambdaInternalWorking.java"
public class com.onlyfullstack.LambdaInternalWorking {
  public com.onlyfullstack.LambdaInternalWorking();
  public static void main(java.lang.String[]);
  private static void lambda$main$0();
}

In this file, we are not able to see our run() method. Lets check the verobse of the class file to get more clarity.

javap -verbose LambdaInternalWorking.class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
Classfile onlyfullstack/LambdaInternalWorking.class
  Last modified 30 Jan, 2019; size 1023 bytes
  MD5 checksum e99d1d2d0eca865f2f46960aad7216f1
  Compiled from "LambdaInternalWorking.java"
public class com.onlyfullstack.LambdaInternalWorking
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#17         // java/lang/Object."<init>":()V
   #2 = InvokeDynamic      #0:#22         // #0:run:()Ljava/lang/Runnable;
   #3 = Fieldref           #23.#24        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = String             #25            // Calling from Lambda
   #5 = Methodref          #26.#27        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #28            // com/onlyfullstack/LambdaInternalWorking
   #7 = Class              #29            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               lambda$main$0
  #15 = Utf8               SourceFile
  #16 = Utf8               LambdaInternalWorking.java
  #17 = NameAndType        #8:#9          // "<init>":()V
  #18 = Utf8               BootstrapMethods
  #19 = MethodHandle       #6:#30         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #20 = MethodType         #9             //  ()V
  #21 = MethodHandle       #6:#31         // invokestatic com/onlyfullstack/LambdaInternalWorking.lambda$main$0:()V
  #22 = NameAndType        #32:#33        // run:()Ljava/lang/Runnable;
  #23 = Class              #34            // java/lang/System
  #24 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #25 = Utf8               Calling from Lambda
  #26 = Class              #37            // java/io/PrintStream
  #27 = NameAndType        #38:#39        // println:(Ljava/lang/String;)V
  #28 = Utf8               com/onlyfullstack/LambdaInternalWorking
  #29 = Utf8               java/lang/Object
  #30 = Methodref          #40.#41        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #31 = Methodref          #6.#42         // com/onlyfullstack/LambdaInternalWorking.lambda$main$0:()V
  #32 = Utf8               run
  #33 = Utf8               ()Ljava/lang/Runnable;
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (Ljava/lang/String;)V
  #40 = Class              #43            // java/lang/invoke/LambdaMetafactory
  #41 = NameAndType        #44:#48        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #42 = NameAndType        #14:#9         // lambda$main$0:()V
  #43 = Utf8               java/lang/invoke/LambdaMetafactory
  #44 = Utf8               metafactory
  #45 = Class              #50            // java/lang/invoke/MethodHandles$Lookup
  #46 = Utf8               Lookup
  #47 = Utf8               InnerClasses
  #48 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #49 = Class              #51            // java/lang/invoke/MethodHandles
  #50 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #51 = Utf8               java/lang/invoke/MethodHandles
{
  public com.onlyfullstack.LambdaInternalWorking();
    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 3: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         5: astore_1
         6: return
      LineNumberTable:
        line 6: 0
        line 7: 6
}
SourceFile: "LambdaInternalWorking.java"
InnerClasses:
     public static final #46= #45 of #49; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #19 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #20 ()V
      #21 invokestatic com/onlyfullstack/LambdaInternalWorking.lambda$main$0:()V
      #20 ()V

We don’t need to understand the whole file, we will just check the piece of code highlighted in yellow color. from line 73 – 80 contains the code written in public static void main() method. on line number 78 JVM is making invokedynamic call to id #2 that is line number 10. So this call will actually insert the run method and execute it without creating an Anonymous class.