jvm 引导方法错误(非法参数异常:错误的参数计数:256)在lambda上使用254个外部局部变量引用

3vpjnl9f  于 2022-11-07  发布在  其他
关注(0)|答案(1)|浏览(86)

来自Java lambda的外部变量引用是作为构造函数参数给出的。Java还允许给一个方法最多255个参数。
例如,编译以下代码:

public class Main {
  public static void main(String[] args) throws InterruptedException {
    var message = "Hello world!";
    var thread = new Thread(() -> System.out.println(message));

    thread.run();
    thread.join();
  }
}

javac会生成以下代码:

final class Main$$Lambda$1 implements java.lang.Runnable {
  private final String arg$1;

  private Main$$Lambda$1(String arg1) {
    super();
    this.arg$1 = arg1;
  }

  public void run() {
    Main.lambda$main$0(this.arg$1);
  }
}

然后,我尝试使用255个外部局部变量从lambda中引用。
但当我运行它时,它抛出了BootstrapMethodError(由IllegalArgumentException引起:错误参数计数256)
我知道引发这个错误的原因是因为构造函数自动地将自己的对象作为参数,因此给了构造函数256个参数。

Exception in thread "main" java.lang.BootstrapMethodError: java.lang.IllegalArgumentException: bad parameter count 256
        at Main.main(Main.java:260)
Caused by: java.lang.IllegalArgumentException: bad parameter count 256
        at java.base/java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:167)
        at java.base/java.lang.invoke.MethodType.checkSlotCount(MethodType.java:223)
        at java.base/java.lang.invoke.MethodType.insertParameterTypes(MethodType.java:437)
        at java.base/java.lang.invoke.MethodType.appendParameterTypes(MethodType.java:461)
        at java.base/java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:233)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:218)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:227)
        at java.base/java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:108)
        at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:4004)
        at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:3960)
        at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:4204)
        at java.base/java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:4152)
        at java.base/java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:615)
        ... 1 more

接下来,我创建了一个引用254个外部局部变量的lambda。当我运行这个函数时,同样的错误再次被抛出。但是给构造函数的参数数量是254 + 1,应该是255。

Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception
        at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:188)
        at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:315)
        at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:281)
        at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:271)
        at Main.main(Main.java:259)
Caused by: java.lang.IllegalArgumentException: bad parameter count 256
        at java.base/java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:167)
        at java.base/java.lang.invoke.MethodType.checkSlotCount(MethodType.java:223)
        at java.base/java.lang.invoke.MethodType.insertParameterTypes(MethodType.java:437)
        at java.base/java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:259)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:233)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:218)
        at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:227)
        at java.base/java.lang.invoke.DirectMethodHandle.makeAllocator(DirectMethodHandle.java:142)
        at java.base/java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:133)
        at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectConstructorCommon(MethodHandles.java:4122)
        at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectConstructor(MethodHandles.java:4106)
        at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:2751)
        at java.base/java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:269)
        at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:341)
        at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:134)
        ... 4 more

我还将var重写为正确的类型,并尝试在java8中运行它,但得到了同样的错误。
为什么我得到一个错误消息,说给出了256个参数?
这个问题是用翻译器从日语翻译过来的,我是基于this article的。

  • 谢谢-谢谢
qv7cva1a

qv7cva1a1#

如果lambda引用了外部的局部变量,那么它需要在lambda创建时捕获这些变量。JVM会动态地创建一个,同时也会动态地创建一个构造函数。这个构造函数的参数是lambda的示例,捕获的局部变量和一个额外的MemberName参数(https://github.com/openjdk/jdk 17 u/blob/master/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java#L256)。
这个隐藏的额外参数就是例外状况的原因。
实际上,这个MemberName参数首先被添加到参数列表中,这就是为什么你的第一个例子在DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256)行失败的原因,如果你试图使用一个带有255个捕获的局部变量的lambda。
在有254个捕获的局部变量的情况下,添加这个MemberName参数是有效的,并且当代码尝试在DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:259)行的位置0插入lambda示例时,代码失败

相关问题