“Invokedynamic工厂创建单例的lambda吗?”

huangapple go评论81阅读模式
英文:

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)发生的位置。exec1exec2 都将创建 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.

huangapple
  • 本文由 发表于 2020年7月26日 03:21:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/63092578.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定