一个Java类中使用的lambdas的最大数量是多少?

时间:2016-11-29 08:18:29

标签: java lambda java-8 jvm

这是一个纯粹的概念性问题。

Java 8中的Lambda将转换为使用invokedynamic调用的方法。

如果对一个类可以拥有的最大方法数有JVM限制,这是否意味着一个类中使用的lambdas的最大数量也受到JVM的严格限制?

这个问题几乎与这个问题相同吗? What is the maximum number of methods a Java class can have?

1 个答案:

答案 0 :(得分:9)

Java语言规范没有规定限制,因此我们只有技术限制。该规范也没有强制要求特定的编译形式,因此即使技术限制也是模糊的。

Lambda表达式被编译为托管lambda表达式主体的类文件的方法,但这不是严格要求的。最值得注意的是,foo -> bar(foo)形式的简单表达式可以像方法引用一样进行编译。此外,可以使用相同的方法编译相同的lambda表达式。这是一种目前尚未发生的优化,它也使调试更加困难,但原则上是允许的。

此外,当智能编译器检测到即将达到限制的不太可能的情况时,它可以开始生成托管lambda主体的辅助类。

使用当前的直接实现,最大方法数(即65535)会影响可能的lambda表达式的最大数量,但这并不意味着我们可以创建65535个lambda表达式。

例如,必须至少有一个包含lambda表达式的(源代码)方法,该方法将创建功能接口的实例。创建站点的最小指令大小是具有五个字节¹的唯一invokedynamic指令。由于方法的最大代码大小为65535且return指令至少需要一个by,因此在一个方法中最多可以有65534/5 == 13106个lambda表达式,因此尝试创建更多需要放置它们采用不同的方法,减少了lambda表达式可用的方法数量。您可以使用嵌套的lambda表达式解决此问题,即x -> y -> zbut even nesting has practical limits

当前编译器使用为每个合成方法生成唯一名称的命名方案,因此它们需要单独的常量池条目。因此,有了独特的实现方法,每个lambda创建站点都需要一个名称条目,名称和类型引用名称,“MethodRef”引用名称和类型以及(总是相同)声明类,方法句柄引用“MethodRef”和引用方法句柄的invokedynamic条目。这使得每个lambda表达式总共有五个常量池条目,并且因为常量池限制为65534个条目,并且我们需要一些条目用于其他目的,计算是65500/5,所以当前编译器实现最大lambda表达式的数量是13,100 。假设他们都有相同的签名......

在使用javac(1.8u111)的练习测试中,我可以使用相同签名的13,098个lambda表达式编译一个类文件,在错误“太多常量”之前,甚至可以生成13,100个禁用生成的调试符号出现了。在该测试类中,我将lambda表达式放入两个构造函数中,因为至少有一个构造函数必须存在,并且两者都可以共享名称条目。我认为,使用标准编译器无法获得更多。

如果要解除命名方案所施加的限制,您仍然必须遵守每个方法必须可区分的规则,因此它必须至少与名称或签名不同,与其他方法不同。如果您尝试达到理论最大值,则必须将 n 不同的方法名称与 m 不同的签名组合以允许n×m个不同的方法,因此65535方法将需要至少256个名称条目和256个签名条目。您仍然拥有唯一的名称和类型组合,因此每个lambda表达式需要其他四个条目,最终会有16,247个可能的lambda表达式。由于它们远小于65535,您可以处理较小的名称和类型组合,即将128个名称与128个签名组合在一起,为创建网站提供更多条目,即具有16311个可能的lambda表达式。如果您将签名字符串滥用为方法名称(在字节代码级别上工作,只要签名不包含引用类型),还可以使用其他名称。

对于(显着)更多,您必须停止为每个lambda表达式生成不同的方法。

¹会产生有效的字节码。在源代码级别,lambda表达式不是语句,因此更多代码,例如需要赋值给变量。