【问题标题】:Why are there 2 stack frames for a lambda invocation?为什么 lambda 调用有 2 个堆栈帧?
【发布时间】:2015-10-04 19:50:12
【问题描述】:

以下代码:

public static void main(String[] args) {
    Collections.singleton(1).stream().forEach(i -> new Exception().printStackTrace());
}

打印:

java.lang.Exception
    at PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)
    at PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)
    at java.util.Collections$2.tryAdvance(Collections.java:4717)
    at java.util.Collections$2.forEachRemaining(Collections.java:4725)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at PrintLambdaStackTrace.main(PrintLambdaStackTrace.java:6)

lambda 调用是如何实现的?为什么有 2 个堆栈帧?

【问题讨论】:

  • 这是个好问题。我会看的,因为我自己对答案很感兴趣。我的猜测是 JVM 在执行时创建了一个匿名类。一个堆栈来自用于创建类的方法,第二个来自类本身。但这只是猜测。

标签: java lambda java-8


【解决方案1】:
PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)

这是一个实现所需接口的生成类。它的accept 方法只是一个存根,委托给在编译时生成并添加到PrintLambdaStackTrace 类的方法。此类在 lambda 链接时生成(第一次需要创建 lambda 实例)。

PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)

这是实际实现 lambda 行为的方法。它属于PrintLambdaStackTrace 类。

【讨论】:

  • 我不会称它为 inner 类。它只是一个生成的类。
  • @Bogdan Calmac:实现方法驻留在定义 lambda 表达式的类中,因此自然可以访问周围的类。此外,磁盘上/jar 文件中没有其他类文件……
  • 是的,就静态生成的代码而言,开销更少。潜在地,在未来的实现中,运行时甚至不需要生成实现接口的成熟类。
  • @Marko Topolnik:好吧,除此之外,唯一将内部类与普通类区分开来的东西,即对外部实例的引用,对于 lambda 类来说并不是强制性的。如果你通过反射请求外部类型,它不会告诉你……
  • 好吧,所有的实例化都发生在运行时(我认为这已经被理解了)。 JVM 做到了,它涉及到invokedynamic 机制。
猜你喜欢
  • 1970-01-01
  • 2021-01-08
  • 2021-05-10
  • 2011-11-20
  • 2021-12-06
  • 2020-10-20
  • 1970-01-01
  • 2020-10-24
  • 2015-10-23
相关资源
最近更新 更多