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

你。 提交于 2019-12-19 02:11:10

问题


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

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

As per 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.

Here Object of anonymous object created,which is subclass of Consumer and can be refer by reference variable consumer,which is Fine.

But I saw Consumer is FunctionalInterface,so I can also do something like this in java8-

Using Lambda

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

OR Using Method Reference

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

What I knows no sub classes or Anonymous classes being created in both the above cases.So this results me two confusion-

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

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


回答1:


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




回答2:


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



回答3:


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.



来源:https://stackoverflow.com/questions/45391745/how-memory-assigns-to-lambda-how-does-it-refered-by-non-super-class-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!