The*_*ous 7 java lambda heap-memory java-8
我正在创建功能接口的实现,下面是我的代码
Consumer<Integer> consumer=new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println(t);
}
};
Run Code Online (Sandbox Code Playgroud)
按照javadoc
类类型T的变量可以包含空引用或对类T的实例或作为T的子类的任何类的引用.
这里创建了匿名对象的对象,它是子类,Consumer可以通过引用变量引用consumer,这很精细.
但我看到Consumer就是FunctionalInterface,所以我也可以做这样的事情在java8-
使用Lambda
Consumer<Integer> consumer=t->System.out.println(t);
Run Code Online (Sandbox Code Playgroud)
或使用方法参考
Consumer<Integer> consumer=System.out::println;
Run Code Online (Sandbox Code Playgroud)
我知道在上述两种情况下都没有创建子类或匿名类.所以这导致我两次混淆 -
1:如果这里consumer没有引用子类或Anonymous类Consumer,那么这不违反上面提到的概念变量只能引用child/self或null?
2:内存如何分配给lamdas以及JVM在运行时如何处理?
首先,让-巴蒂斯特 (Jean-Baptiste) 向您展示了为什么该作业首先起作用。
现在,我认为您缺少的部分是生成的类Consumerin case ofConsumer<Integer> consumer = t -> System.out.println(t);仅在运行时可见,因为invokedynamic.
使用标志运行您的课程:
java -Djdk.internal.lambda.dumpProxyClasses=/Your/Path
Run Code Online (Sandbox Code Playgroud)
您会注意到有一个生成的类(在类的包名称的文件夹路径中)包含.class类似这样的文件SOQuestion$$Lambda$1.class。
如果你反编译,你会看到它实际上是一个实现的类Consumer:
final class org.eugene.so.SOQuestion$$Lambda$1
implements java.util.function.Consumer {
public void accept(java.lang.Object);
}
Run Code Online (Sandbox Code Playgroud)
如果您查看定义 lambda 的生成的字节码,您将看到 lambda 表达式实际上是去糖化为静态方法(对于非捕获 lambda,如果它是捕获 lambda,它也可以是非静态的)。它的代码:
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
Run Code Online (Sandbox Code Playgroud)
至于它如何VM管理内存,对于非捕获 lambda,您将获得一个单例,对于捕获 lambda,您将获得每个调用的新实例。绝对必读的在这里
需要参考SE8 15.27:
15.27.3. Lambda 表达式的类型
如果 T 是函数接口类型(第 9.8 节)并且表达式与从 T 派生的基础目标类型的函数类型一致,则 lambda 表达式在具有目标类型 T 的赋值上下文、调用上下文或转换上下文中兼容。
运行时处理使用棘手的事情invokedynamic。让我们检查一些代码:
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
}
}
Run Code Online (Sandbox Code Playgroud)
执行给出:
> java L
class R
there
class L$1
elsewhere
class L$$Lambda$1/791452441
here
Run Code Online (Sandbox Code Playgroud)
对于前两个,毫不奇怪,方法接收到的对象的类execute是(按给定顺序)R( 的子类型Runnable),L$1( 的匿名子类型Runnable,和L$$Lambda$1/791452441(Runnable在运行时从 lambda 构造的子类型)。请注意,在 lambda 没有.class文件的情况下,类型是在运行时通过特殊构造构造的。让我们检查字节码:
> 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
Run Code Online (Sandbox Code Playgroud)
第一个有趣的部分是以下代码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
Run Code Online (Sandbox Code Playgroud)
如您所见,接口的显式实现或匿名实现之间没有区别。最后一个只涉及类命名技巧(L$1),但两者的使用方式相同,通过invokestatic.
有趣的案例是第三个(lambda 案例),它涉及invokedynamic和 then invokestatic。请注意,这些invokestatic调用与前两次调用(方法run)中的方法相同。
粗略地说,第一次invokedynamic调用 bootstrap 方法时,会调用它来构造一个CallSite(请参阅Java API 中的 CallSite),然后将进一步使用它来执行 lambda 的代码。请参阅此处的引导程序调用:
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
Run Code Online (Sandbox Code Playgroud)
以及调用站点引用的代码:
#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
Run Code Online (Sandbox Code Playgroud)
问:如果这里的消费者不是指消费者的子类或匿名类......?
实际上,子类是在Linkage阶段通过invokedynamc指令引入的。
链接可能涉及动态加载实现目标接口的新类。的
CallSite可以被认为是“工厂”为函数对象,因此这些连接方法被称为“metafactories”。
问:内存如何分配给 lamdas 以及 JVM 在运行时如何处理?
它通过三个阶段按顺序进行:
有关更多详细信息,您可以进一步查看LambdaMetafactory。
| 归档时间: |
|
| 查看次数: |
747 次 |
| 最近记录: |