Java lambda 表达式每次执行时都会在堆上创建一个对象吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27524445/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 04:30:35  来源:igfitidea点击:

Does a lambda expression create an object on the heap every time it's executed?

javalambdajava-8

提问by Bastian Voigt

When I iterate over a collection using the new syntactic sugar of Java 8, such as

当我使用 Java 8 的新语法糖迭代集合时,例如

myStream.forEach(item -> {
  // do something useful
});

Isn't this equivalent to the 'old syntax' snippet below?

这不等同于下面的“旧语法”片段吗?

myStream.forEach(new Consumer<Item>() {
  @Override
  public void accept(Item item) {
    // do something useful
  }
});

Does this mean a new anonymous Consumerobject is created on the heap every time I iterate over a collection? How much heap space does this take? What performance implications does it have? Does it mean I should rather use the old style for loops when iterating over large multi-level data structures?

这是否意味着Consumer每次迭代集合时都会在堆上创建一个新的匿名对象?这需要多少堆空间?它有什么性能影响?这是否意味着在迭代大型多级数据结构时我应该使用旧样式的 for 循环?

采纳答案by Holger

It is equivalent but not identical. Simply said, if a lambda expression does not capture values, it will be a singleton that is re-used on every invocation.

它等价但不完全相同。简单地说,如果一个 lambda 表达式不捕获值,它将是一个在每次调用时重复使用的单例。

The behavior is not exactly specified. The JVM is given big freedom on how to implement it. Currently, Oracle's JVM creates (at least) one instance per lambda expression (i.e. doesn't share instance between different identical expressions) but creates singletons for all expressions which don't capture values.

行为没有完全指定。JVM 在如何实现它方面有很大的自由。目前,Oracle 的 JVM 为每个 lambda 表达式创建(至少)一个实例(即不在不同的相同表达式之间共享实例),但为所有不捕获值的表达式创建单例。

You may read this answerfor more details. There, I not only gave a more detailed description but also testing code to observe the current behavior.

您可以阅读此答案以获取更多详细信息。在那里,我不仅给出了更详细的描述,而且还测试了代码以观察当前行为。



This is covered by The Java? Language Specification, chapter “15.27.4. Run-time Evaluation of Lambda Expressions

这是由 Java 涵盖的吗?语言规范,章节“ 15.27.4。Lambda 表达式的运行时评估

Summarized:

总结:

These rules are meant to offer flexibility to implementations of the Java programming language, in that:

  • A new object need not be allocated on every evaluation.

  • Objects produced by different lambda expressions need not belong to different classes (if the bodies are identical, for example).

  • Every object produced by evaluation need not belong to the same class (captured local variables might be inlined, for example).

  • If an "existing instance" is available, it need not have been created at a previous lambda evaluation (it might have been allocated during the enclosing class's initialization, for example).

这些规则旨在为 Java 编程语言的实现提供灵活性,因为:

  • 不需要在每次评估时分配一个新对象。

  • 由不同 lambda 表达式生成的对象不必属于不同的类(例如,如果主体相同)。

  • 评估产生的每个对象不必属于同一个类(例如,捕获的局部变量可能是内联的)。

  • 如果“现有实例”可用,则不必在之前的 lambda 评估中创建它(例如,它可能已在封闭类的初始化期间分配)。

回答by aalku

You are passing a new instance to the forEachmethod. Every time you do that you create a new object but not one for every loop iteration. Iteration is done inside forEachmethod using the same 'callback' object instance until it is done with the loop.

您正在向该forEach方法传递一个新实例。每次这样做时,您都会创建一个新对象,但不会为每次循环迭代创建一个对象。迭代是在forEach方法内部使用相同的“回调”对象实例完成的,直到循环完成。

So the memory used by the loop does not depend on the size of the collection.

所以循环使用的内存不依赖于集合的大小。

Isn't this equivalent to the 'old syntax' snippet?

这不等同于“旧语法”片段吗?

Yes. It has slight differences at a very low level but I don't think you should care about them. Lamba expressions use the invokedynamic feature instead of anonymous classes.

是的。它在非常低的水平上略有不同,但我认为您不应该关心它们。Lamba 表达式使用invokedynamic 功能而不是匿名类。

回答by Marko Topolnik

When an instance representing the lambda is created sensitively depends on the exact contents of your lambda's body. Namely, the key factor is what the lambda capturesfrom the lexical environment. If it doesn't capture any state which is variable from creation to creation, then an instance will not be created each time the for-each loop is entered. Instead a synthetic method will be generated at compile time and the lambda use site will just receive a singleton object that delegates to that method.

何时敏感地创建代表 lambda 的实例取决于 lambda 主体的确切内容。也就是说,关键因素是 lambda从词法环境中捕获的内容。如果它没有捕获任何从创建到创建可变的状态,那么每次进入 for-each 循环时都不会创建实例。相反,将在编译时生成合成方法,并且 lambda 使用站点将仅接收委托给该方法的单例对象。

Further note that this aspect is implementation-dependent and you can expect future refinements and advancements on HotSpot towards greater efficiency. There are general plans to e.g. make a lightweight object without a full corresponding class, which has just enough information to forward to a single method.

进一步请注意,这方面取决于实现,您可以期待 HotSpot 未来的改进和改进以提高效率。有一些通用的计划,例如制作一个没有完整对应类的轻量级对象,它只有足够的信息来转发到单个方法。

Here is a good, accessible in-depth article on the topic:

这是关于该主题的一篇很好的、可访问的深入文章:

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood