Вопрос-ответ

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

Создает ли лямбда-выражение объект в куче каждый раз, когда оно выполняется?

Когда я выполняю итерацию по коллекции, используя новый синтаксический сахар Java 8, например

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

Разве это не эквивалентно приведенному ниже фрагменту "старого синтаксиса"?

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

Означает ли это, что новый анонимный Consumer объект создается в куче каждый раз, когда я перебираю коллекцию? Сколько места в куче это занимает? Какие последствия для производительности это имеет? Означает ли это, что я должен использовать старый стиль циклов for при переборе больших многоуровневых структур данных?

Переведено автоматически
Ответ 1

Это эквивалентно, но не идентично. Проще говоря, если лямбда-выражение не фиксирует значения, это будет синглтон, который повторно используется при каждом вызове.

Поведение точно не определено. JVM предоставлена большая свобода в том, как его реализовать. В настоящее время JVM Oracle создает (по крайней мере) один экземпляр для каждого лямбда-выражения (т. Е. Не разделяет экземпляр между разными идентичными выражениями), но создает синглтоны для всех выражений, которые не фиксируют значения.

Вы можете прочитать этот ответ для получения более подробной информации. Там я не только дал более подробное описание, но и тестировал код для наблюдения за текущим поведением.


Это описано в спецификации языка Java®, глава “15.27.4. Оценка лямбда-выражений во время выполнения

Резюмировано:


Эти правила предназначены для обеспечения гибкости реализаций языка программирования Java, поскольку:



  • При каждом вычислении не обязательно выделять новый объект.



  • Объекты, созданные разными лямбда-выражениями, не обязательно должны принадлежать разным классам (например, если тела идентичны).



  • Каждый объект, созданный в результате вычисления, необязательно должен принадлежать одному и тому же классу (например, захваченные локальные переменные могут быть встроенными).



  • Если доступен "существующий экземпляр", его необязательно создавать при предыдущей оценке лямбда-выражения (например, он мог быть выделен во время инициализации заключающего класса).




Ответ 2

Когда создается экземпляр, представляющий лямбда-выражение, зависит от точного содержимого тела вашего лямбда-выражения. А именно, ключевым фактором является то, что лямбда-выражение захватывает из лексической среды. Если оно не фиксирует какое-либо состояние, которое изменяется от создания к созданию, то экземпляр не будет создаваться каждый раз, когда вводится цикл for-each . Вместо этого во время компиляции будет сгенерирован синтетический метод, и сайт использования лямбда-выражения просто получит одноэлементный объект, который делегирует этот метод.

Далее обратите внимание, что этот аспект зависит от реализации, и вы можете ожидать будущих усовершенствований HotSpot в направлении повышения эффективности. Существуют общие планы, например, создать легковесный объект без полного соответствующего класса, в котором достаточно информации для пересылки в один метод.

Вот хорошая, доступная подробная статья на эту тему:

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

Ответ 3

Вы передаете новый экземпляр методу forEach. Каждый раз, когда вы это делаете, вы создаете новый объект, но не по одному для каждой итерации цикла. Итерация выполняется внутри forEach метода с использованием того же экземпляра объекта 'callback', пока это не будет выполнено с помощью цикла.

Таким образом, память, используемая циклом, не зависит от размера коллекции.


Разве это не эквивалентно фрагменту 'old syntax'?


ДА. У него есть небольшие отличия на очень низком уровне, но я не думаю, что они должны вас волновать. Выражения Lamba используют функцию invokedynamic вместо анонимных классов.

java lambda java-8