BootstrapMethodError(IllegalArgumentException: bad parameter count: 256) on lambda with 254 outside local variable references

BootstrapMethodError(IllegalArgumentException: bad parameter count: 256) on lambda with 254 outside local variable references

来自 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 implements java.lang.Runnable {
  private final String arg;

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

  public void run() {
    Main.lambda$main[=11=](this.arg);
  }
}

然后,我尝试使用 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 个参数?

这个问题是从日语中用 t运行slator 提出来的 运行,我是基于 this article.

谢谢。

如果 lambda 引用外部局部变量,则需要在 lambda 创建时捕获它们。 JVM为此动态创建一个,也为这个class动态创建一个构造函数。构造函数将 lambda 实例、捕获的局部变量和一个额外的 MemberName 参数 (https://github.com/openjdk/jdk17u/blob/master/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java#L256).

作为参数

这个隐藏的额外参数是异常的原因。


实际上,这个MemberName参数首先添加到参数列表中。如果您尝试使用具有 255 个捕获的局部变量的 lambda,这就是您的第一个示例在 DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256) 行失败的原因。

在有 254 个捕获的局部变量的情况下,添加此 MemberName 参数有效,并且代码在尝试在行 DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:259)

的位置 0 处插入 lambda 实例时失败