Loop.times(5, () -> {
System.out.println("looping");
});
它有效地编译到哪个?
for(int i = 0; i < 5; i++)
System.out.println("looping");
或类似
new CallableInterfaceImpl(){
public void call(){
for(int i = 0; i < 5; i++)
System.out.println("looping");
}
}.call();
那么它会替换(内联类),还是实际创建一个匿名类?
答案 0 :(得分:47)
VM决定如何实现lambda,而不是编译器。
请参阅Translation of Lambda Expressions中的Translation strategy
部分。
从简单的编译或性能来看,我们不是生成字节码来创建实现lambda表达式的对象(例如调用内部类的构造函数),而是描述构造lambda的配方,并将实际构造委托给语言运行库。该配方在invokedynamic指令的静态和动态参数列表中进行编码。
for
构造是最有效的方法(但是测试中的性能差异非常小)。
插件:
我创建并反汇编了两个例子:
for (String string: Arrays.asList("hello")) {
System.out.println(string);
}
反汇编的字节码,常量和其他信息:
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
}
和
Arrays.asList("hello").forEach(p -> {System.out.println(p);});
反汇编的字节码,常量和其他信息:
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
}
对于Lambda示例,编译器生成的类文件更复杂,更大(771b对1262b)。
答案 1 :(得分:0)
开发人员编写的Lambda语法已分解为编译期间生成的JVM级别指令。因此,构造lambda的实际责任将推迟到运行时。
为了使Java的编译时间更少严格,并允许JVM语言在运行时绑定代码,Java 7向JVM引入了新的invokedynamic字节码指令,并引入了新的java.lang.invoke API包。 invokedynamic通过动态方法调用促进了动态语言(对于JVM)的实现。 Lambda工程师为Lambda选择了invokedynamic方法。让我们看看Lambda被编译成什么:
程序:
package com.onlyfullstack;
public class LambdaInternalWorking {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Calling from Lambda");
}
}
让我们编译以下程序:
javac LambdaInternalWorking.java
让我们反汇编.class文件以查看内容:
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();
}
在此文件中,我们看不到我们的run()方法。让我们检查类文件的Verobse,以获得更多的清晰度。
javap -verbose LambdaInternalWorking.class
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
我们不需要了解整个文件,我们只需要检查黄色突出显示的代码即可。从第73-80行开始,包含用public static void main()方法编写的代码。在第78行,JVM正在对第10行的id#2进行invokedynamic调用,因此,此调用实际上将插入run方法并执行它,而无需创建Anonymous类。
有关更多信息,请参见以下链接: https://onlyfullstack.blogspot.com/2019/02/lambda-expression-tutorial-in-java-8.html
https://onlyfullstack.blogspot.com/2019/02/how-lambda-internally-works-in-java-8.html
答案 2 :(得分:0)
Java编译器将为未显式或隐式声明的代码构造生成综合方法。
我们知道,lambda表达式/函数是函数接口中抽象方法的匿名类方法实现,如果我们看到带有lambda表达式的已编译类文件的字节码,则无需创建一个新对象来包装Lambda函数,它使用新的 INVOKEDYNAMIC 指令将此调用站点动态链接到实际Lambda函数,该函数将转换为private static synthetic lambda$0(Ljava/lang/String;)V
,它将接受字符串作为参数
private static synthetic lambda$0(Ljava/lang/String;)V
GETSTAIC java/lang/System.out: Ljava/io/PrintStream;
ALOAD 0
INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V
RETURN
示例:list.forEach(x-> System.out.println(x));
如上所述,该lambda表达式x-> System.out.println(x)
被转换为私有静态合成块。 但是当我们运行Java类时,如何对列表中的每个元素调用此方法?,因为forEach接受了Consumer功能接口对象,请参考下面的lambda表达式链接字节代码。
INVOKEDYNAMIC accept()Ljava/util/function/Consumer;
[
java/lang/invoke/LambdaMetaFactory.metafactory(Ljava/lang/invokeMethodHandler$Lookup.Ljava/lang/invoke/CallSite..
//arguments
(Ljava/lang/Object;)V
//INVOKESTATIC
com/<Classname>.lambda$)(Ljava/lang/String;)V,
(Ljava/lang/String;)V
]
java.lang.invoke.LambdaMetaFactory :此类提供了两种形式的链接方法:
这些链接方法旨在支持对Java语言中的lambda表达式和方法引用进行求值。 对于源代码中的每个lambda表达式或方法引用,都有一个目标类型,它是一个功能接口。 计算lambda表达式会产生其目标类型的对象。推荐的评估lambda表达式的机制是将lambda主体解糖为一种方法,调用一个invokedynamic调用站点,该站点的静态参数列表描述了功能接口的唯一方法和已废止的实现方法,并返回一个对象(lambda对象)实现目标类型。 注意(对于方法引用,实现方法只是被引用的方法;无需重复使用。)