Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Memory assigns to lambda | How does it refered,by non super class reference variable

I was creating the implementation of a functional interface, below is my code:

Consumer<Integer> consumer = new Consumer<Integer>() {
    @Override
    public void accept(Integer t) {
        System.out.println(t);
    }
};

As per the Java Documentation (javadoc)

A variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T.

In the code above, the anonymous object is created, which is a subclass of Consumer, and can be referred to by reference variable consumer, which is fine.

But I saw Consumer is a FunctionalInterface, so I can also do something like this in Java 8:

Using Lambda

Consumer<Integer> consumer = t -> System.out.println(t);

OR Using Method Reference

Consumer<Integer> consumer = System.out::println;

From what I know, no sub classes or anonymous classes are being created in both of the above cases. This result leaves me with two confusions:

1 : In the result of the lambda test, the variable consumer is not referring to subclass or Anonymous class of Consumer, so isn't this violating the above mentioned concept variable which can only refer child/self or null?

2 : How is memory assigned to lambdas, and how does the JVM handle such at run time?

like image 719
TheCurious Avatar asked Jul 29 '17 16:07

TheCurious


3 Answers

First of all Jean-Baptiste has shown you why the assignment works in the first place.

Now the part that I think that you are missing is the fact that the generated class of Consumer in case of Consumer<Integer> consumer = t -> System.out.println(t); is only visible at runtime due to invokedynamic.

Run your class with a flag :

java -Djdk.internal.lambda.dumpProxyClasses=/Your/Path

And you will notice that there is a generated class (inside a path of folders from your package name of the class) that contains a .class file sort of like this SOQuestion$$Lambda$1.class.

If you decompile that you will see that it's actually a class that implements Consumer:

final class org.eugene.so.SOQuestion$$Lambda$1 
            implements java.util.function.Consumer {
     public void accept(java.lang.Object);
}

If you look at the generated byte code where your lambda is defined, you will see that the lambda expression is actually de-sugared to a static method (for non-captureting lambdas, it could also be non-static if it is a capturing lambda). The code for it:

private static void lambda$main$0(java.lang.Integer);
Code:
   0: getstatic     #3  // Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   7: return

As far as how the VM manages memory for it, for an non-capturing lambda you will get a singleton, for a capturing-lambda you will get a new instance of each call. The absolute must read is here

like image 65
Eugene Avatar answered Nov 17 '22 12:11

Eugene


You need to refer to SE8 15.27:

15.27.3. Type of a Lambda Expression

A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

Runtime handling use tricky things with invokedynamic. Let examine some code:

import java.util.function.*;

class R implements Runnable {
  public void run() { System.out.println("there"); }
}

public class L {
  public static void execute(Runnable r) {
    System.out.println(r.getClass());
    r.run();
  }
  public static void main(String []a) {
    execute(new R()); // subclass
    execute(new Runnable() { // anonymous subclass
        public void run() { System.out.println("elsewhere"); }
      });
    execute(() -> System.out.println("here")); // lambda
  }
}

Execution gives:

> java L
class R
there
class L$1
elsewhere
class L$$Lambda$1/791452441
here

For the first two there is no surprise, the class of the object received by method execute is (in the given order) R (the subtype of Runnable), L$1 (the anonymous subtype of Runnable, and L$$Lambda$1/791452441 (the subtype of Runnable constructed at run time from the lambda). Note that in the case of lambda there is no .class file, the type is constructed at runtime by special construct. Let's examine the bytecode:

> javap -c -v L
Classfile /private/tmp/L.class
  Last modified 1 août 2017; size 1234 bytes
  MD5 checksum 9680a2bc143d25344979bae00fff3db7
  Compiled from "L.java"
public class L
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #15.#28        // java/lang/Object."<init>":()V
   #2 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #15.#31        // java/lang/Object.getClass:()Ljava/lang/Class;
   #4 = Methodref          #32.#33        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #5 = InterfaceMethodref #34.#35        // java/lang/Runnable.run:()V
   #6 = Class              #36            // R
   #7 = Methodref          #6.#28         // R."<init>":()V
   #8 = Methodref          #14.#37        // L.execute:(Ljava/lang/Runnable;)V
   #9 = Class              #38            // L$1
  #10 = Methodref          #9.#28         // L$1."<init>":()V
  #11 = InvokeDynamic      #0:#43         // #0:run:()Ljava/lang/Runnable;
  #12 = String             #44            // here
  #13 = Methodref          #32.#45        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #14 = Class              #46            // L
  #15 = Class              #47            // java/lang/Object
  #16 = Utf8               InnerClasses
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               execute
  #22 = Utf8               (Ljava/lang/Runnable;)V
  #23 = Utf8               main
  #24 = Utf8               ([Ljava/lang/String;)V
  #25 = Utf8               lambda$main$0
  #26 = Utf8               SourceFile
  #27 = Utf8               L.java
  #28 = NameAndType        #17:#18        // "<init>":()V
  #29 = Class              #48            // java/lang/System
  #30 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #31 = NameAndType        #51:#52        // getClass:()Ljava/lang/Class;
  #32 = Class              #53            // java/io/PrintStream
  #33 = NameAndType        #54:#55        // println:(Ljava/lang/Object;)V
  #34 = Class              #56            // java/lang/Runnable
  #35 = NameAndType        #57:#18        // run:()V
  #36 = Utf8               R
  #37 = NameAndType        #21:#22        // execute:(Ljava/lang/Runnable;)V
  #38 = Utf8               L$1
  #39 = Utf8               BootstrapMethods
  #40 = MethodHandle       #6:#58         // 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;
  #41 = MethodType         #18            //  ()V
  #42 = MethodHandle       #6:#59         // invokestatic L.lambda$main$0:()V
  #43 = NameAndType        #57:#60        // run:()Ljava/lang/Runnable;
  #44 = Utf8               here
  #45 = NameAndType        #54:#61        // println:(Ljava/lang/String;)V
  #46 = Utf8               L
  #47 = Utf8               java/lang/Object
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               getClass
  #52 = Utf8               ()Ljava/lang/Class;
  #53 = Utf8               java/io/PrintStream
  #54 = Utf8               println
  #55 = Utf8               (Ljava/lang/Object;)V
  #56 = Utf8               java/lang/Runnable
  #57 = Utf8               run
  #58 = Methodref          #62.#63        // 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;
  #59 = Methodref          #14.#64        // L.lambda$main$0:()V
  #60 = Utf8               ()Ljava/lang/Runnable;
  #61 = Utf8               (Ljava/lang/String;)V
  #62 = Class              #65            // java/lang/invoke/LambdaMetafactory
  #63 = NameAndType        #66:#69        // 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;
  #64 = NameAndType        #25:#18        // lambda$main$0:()V
  #65 = Utf8               java/lang/invoke/LambdaMetafactory
  #66 = Utf8               metafactory
  #67 = Class              #71            // java/lang/invoke/MethodHandles$Lookup
  #68 = Utf8               Lookup
  #69 = 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;
  #70 = Class              #72            // java/lang/invoke/MethodHandles
  #71 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #72 = Utf8               java/lang/invoke/MethodHandles
{
  public L();
    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 9: 0

  public static void execute(java.lang.Runnable);
    descriptor: (Ljava/lang/Runnable;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: invokevirtual #3                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        10: aload_0
        11: invokeinterface #5,  1            // InterfaceMethod java/lang/Runnable.run:()V
        16: return
      LineNumberTable:
        line 11: 0
        line 12: 10
        line 13: 16

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #6                  // class R
         3: dup
         4: invokespecial #7                  // Method R."<init>":()V
         7: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        10: new           #9                  // class L$1
        13: dup
        14: invokespecial #10                 // Method L$1."<init>":()V
        17: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        20: invokedynamic #11,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
        25: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        28: return
      LineNumberTable:
        line 15: 0
        line 16: 10
        line 19: 20
        line 20: 28
}
SourceFile: "L.java"
InnerClasses:
     static #9; //class L$1
     public static final #68= #67 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #40 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:
      #41 ()V
      #42 invokestatic L.lambda$main$0:()V
      #41 ()V

The first interesting part is the code of main:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: new           #6                  // class R
         3: dup
         4: invokespecial #7                  // Method R."<init>":()V
         7: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        10: new           #9                  // class L$1
        13: dup
        14: invokespecial #10                 // Method L$1."<init>":()V
        17: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        20: invokedynamic #11,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;
        25: invokestatic  #8                  // Method execute:(Ljava/lang/Runnable;)V
        28: return

As you can observe there is no difference in between the explicit implementation of the interface or the anonymous one. The last only involves class naming trickery (L$1), but both are used the same way, through invokestatic.

Interesting case is the third (the lambda one) which involves invokedynamic and then invokestatic. Note that the invokestatic calls the same method as in the two previous calls (the method run).

Roughly, the first time the invokedynamic is called a bootstrap method is called to construct a CallSite (see CallSite in Java API) which will then be used further to execute the code of the lambda. See the bootstrap call here:

BootstrapMethods:
  0: #40 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:
      #41 ()V
      #42 invokestatic L.lambda$main$0:()V
      #41 ()V

and the code referred by the call site:

  #41 = MethodType         #18            //  ()V
  #42 = MethodHandle       #6:#59         // invokestatic L.lambda$main$0:()V
  #43 = NameAndType        #57:#60        // run:()Ljava/lang/Runnable;
  #44 = Utf8               here
  #45 = NameAndType        #54:#61        // println:(Ljava/lang/String;)V
  #46 = Utf8               L
  #47 = Utf8               java/lang/Object
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               getClass
  #52 = Utf8               ()Ljava/lang/Class;
  #53 = Utf8               java/io/PrintStream
  #54 = Utf8               println
  #55 = Utf8               (Ljava/lang/Object;)V
  #56 = Utf8               java/lang/Runnable
  #57 = Utf8               run
like image 34
Jean-Baptiste Yunès Avatar answered Nov 17 '22 12:11

Jean-Baptiste Yunès


Q: If here consumer is not referring to subclass or Anonymous class of Consumer ....?

In fact, the subclass is introduced in Linkage phase by invokedynamc instruction.

Linkage may involve dynamically loading a new class that implements the target interface. The CallSite can be considered a "factory" for function objects and so these linkage methods are referred to as "metafactories".

Q: How memory assign to lamdas and how does JVM handle such at run time?

It proceeds in order through three phases:

  • Linkage - may involve dynamically loading a new class that implements the target interface.
  • Capture - producing a function object.
  • Invocation - an implemented interface method is invoked on a function object.

For the more details, you can see LambdaMetafactory as further.

like image 5
holi-java Avatar answered Nov 17 '22 13:11

holi-java