HotSpot可以内联lambda函数调用吗?

考虑代码:

someList.forEach(x -> System.out.format("element %s", x)); 

从理论上讲,应该可以通过首先内联forEach方法来内联此代码并消除间接函数调用,然后在内联的forEach代码中内联lambda函数体。

HotSpot能够执行此优化吗? 在特定情况下是否执行了哪些限制?

您的lambda表达式被编译为普通方法,而JRE将生成一个实现function接口并调用该方法的类。 在当前的HotSpot版本中,这个生成的类几乎像普通类一样工作,主要区别在于它可以调用private目标方法,并且不会被ClassLoader反向引用。

这些属性都没有阻碍优化,最终,您只有一系列普通的方法调用。 使用当前JVM的此类代码的最大障碍是内联限制,关于最大深度(默认为九个嵌套方法IIRC)和最大结果代码大小。 其中一些默认值非常陈旧,并且自上次定义以来未进行修订。 但是,这样的限制可能会影响非常长的流管道,而不是像普通的forEach那样的用例。

因此,一般的答案是HotSpot能够执行此类优化,但与所有优化一样,它会让您的代码运行几次,然后确定它是否对性能至关重要并执行优化(如果是)。

这实际上很容易certificate。 这是一些非常简单的代码:

  for (int i = 0; i < 100_000; ++i) { Stream.of(1, 2, 3, 4) .map(x -> x * 2) .collect(Collectors.toList()); } 

当我编译它时,我可以看到为lambda表达式生成的脱糖方法(通过javap )被调用: lambda$main$0 (在jdk-9中,但这并不重要)。

然后我可以简单地运行以下代码:

  java -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining -XX:CompileCommand="print, *.lambda" InlineLambdaTest > inline.txt 

看文件有这样的行:

  Inline::lambda$main$0 (10 bytes) inline (hot) 

因此,这种方法的内联工作是通常的方式。 请注意,将会有更多以...lambda...开头的行...lambda...因为内部还有许多其他地方使用lambda表达式,这些地方也被认为是热门的。