英文:
Invokedynamic factory creates lambdas as a singleton?
问题
例如,我有两个 lambda 表达式:
Runnable exec1 = () -> {
System.out.print("Hi from lambda");
};
Runnable exec2 = () -> {
System.out.print("Hi from lambda");
};
Invokedynamic 操作符将使用特殊工厂创建它:
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;
但是我在字节码读取方面遇到了一些问题。这是否意味着在这种情况下该工厂会缓存 lambda 创建(exec2
将重用实例)?
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 6 L0
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
test/Main.lambda$main$0()V,
()V
]
ASTORE 1
L1
LINENUMBER 10 L1
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
test/Main.lambda$main$1()V,
()V
]
ASTORE 2
L2
LINENUMBER 13 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE exec Ljava/lang/Runnable; L1 L3 1
LOCALVARIABLE exec2 Ljava/lang/Runnable; L2 L3 2
MAXSTACK = 1
MAXLOCALS = 3
// access flags 0x100A
private static synthetic lambda$main$1()V
L0
LINENUMBER 11 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hi from lambda"
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
L1
LINENUMBER 12 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 0
// access flags 0x100A
private static synthetic lambda$main$0()V
L0
LINENUMBER 7 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hi from lambda"
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
L1
LINENUMBER 8 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
英文:
For example, I have two lambdas:
Runnable exec1 = () -> {
System.out.print("Hi from lambda");
};
Runnable exec2 = () -> {
System.out.print("Hi from lambda");
};
Invokedynamic operator will create it with special factory
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;
But I have some problems with bytecode reading. Does it mean, that in this case this factory will cache lambda creation (and exec2
will reuse instance)?
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 6 L0
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
test/Main.lambda$main$0()V,
()V
]
ASTORE 1
L1
LINENUMBER 10 L1
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
test/Main.lambda$main$1()V,
()V
]
ASTORE 2
L2
LINENUMBER 13 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE exec Ljava/lang/Runnable; L1 L3 1
LOCALVARIABLE exec2 Ljava/lang/Runnable; L2 L3 2
MAXSTACK = 1
MAXLOCALS = 3
// access flags 0x100A
private static synthetic lambda$main$1()V
L0
LINENUMBER 11 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hi from lambda"
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
L1
LINENUMBER 12 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 0
// access flags 0x100A
private static synthetic lambda$main$0()V
L0
LINENUMBER 7 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hi from lambda"
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
L1
LINENUMBER 8 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
答案1
得分: 2
你需要首先理解什么是 调用点(call-site)
,在我看来,要能够理解缓存(caching)
发生的位置。exec1
和 exec2
都将创建 Runnable
接口的两个单独实例;它们都将被缓存在调用点
上。也许这个小片段会有所帮助:
public static void main(String[] args) {
useStatelessLambda1();
useStatelessLambda1();
useStatelessLambda2();
useStatelessLambda2();
}
static void useStatelessLambda1() {
Runnable exec1 = () -> {
System.out.print("Hi from lambda");
};
System.out.print(exec1.hashCode() + " ");
exec1.run();
System.out.println("\n");
}
static void useStatelessLambda2() {
Runnable exec2 = () -> {
System.out.print("Hi from lambda");
};
System.out.print(exec2.hashCode() + " ");
exec2.run();
System.out.println("\n");
}
运行这段代码会显示:
1878246837 Hi from lambda
1878246837 Hi from lambda
1995265320 Hi from lambda
1995265320 Hi from lambda
分离的实例,但都缓存于调用点。
无论如何,查看字节码不会告诉你关于这个的任何信息。你可以查看 `invokedynamic` 将使用的引导方法:`LambdaMetafactory::metafactory`,并理解它的作用。
英文:
You need to understand what a call-site
is, first, imo; to be able to understand where caching happens. Both exec1
and exec2
will create two separate instances of a Runnable
interface; both will be cached on the call-site
. May be this little snippet will help:
public static void main(String[] args) {
useStatelessLambda1();
useStatelessLambda1();
useStatelessLambda2();
useStatelessLambda2();
}
static void useStatelessLambda1() {
Runnable exec1 = () -> {
System.out.print("Hi from lambda");
};
System.out.print(exec1.hashCode() + " ");
exec1.run();
System.out.println("\n");
}
static void useStatelessLambda2() {
Runnable exec2 = () -> {
System.out.print("Hi from lambda");
};
System.out.print(exec2.hashCode() + " ");
exec2.run();
System.out.println("\n");
}
Running this reveals:
1878246837 Hi from lambda
1878246837 Hi from lambda
1995265320 Hi from lambda
1995265320 Hi from lambda
separate instances, but both cached on the call-site.
Either way, looking at the byte-code will not tell you anything about that. what you could look at is the bootstrap method that invokedynamic
will use : LambdaMetafactory::metafactory
and understand what that will do.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论