web-dev-qa-db-fra.com

Une expression lambda crée-t-elle un objet sur le tas chaque fois qu'il est exécuté?

Quand j'itère une collection en utilisant le nouveau sucre syntaxique de Java 8, tel que

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

N'est-ce pas l'équivalent de l'extrait de code "ancienne syntaxe" ci-dessous?

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

Cela signifie-t-il qu'un nouvel objet Consumer anonyme est créé sur le tas chaque fois que je parcours une collection? Combien d'espace tas cela prend-il? Quelles sont les implications en termes de performances? Est-ce que cela signifie que je devrais plutôt utiliser l'ancien style pour les boucles lors d'une itération sur de grandes structures de données multiniveaux?

159
Bastian Voigt

C'est équivalent mais pas identique. En termes simples, si une expression lambda ne capture pas de valeurs, ce sera un singleton qui sera réutilisé à chaque invocation.

Le comportement n'est pas spécifié exactement. La JVM a une grande liberté quant à sa mise en œuvre. Actuellement, la machine virtuelle Java d’Oracle crée (au moins) une instance par expression lambda (c’est-à-dire ne partage pas d’instance entre différentes expressions identiques), mais crée un singleton pour toutes les expressions qui ne capturent pas de valeurs.

Vous pouvez lire cette réponse pour plus de détails. Là-bas, j'ai non seulement donné une description plus détaillée, mais aussi testé du code pour observer le comportement actuel.


Ceci est couvert par la spécification de langage Java®, au chapitre " 15.27.4. Évaluation des expressions lambda au moment de l'exécution ".

Résumé:

Ces règles sont conçues pour offrir une flexibilité dans l’implémentation du langage de programmation Java), en ce sens que:

  • Un nouvel objet ne doit pas nécessairement être attribué à chaque évaluation.

  • Les objets produits par différentes expressions lambda ne doivent pas nécessairement appartenir à des classes différentes (si les corps sont identiques, par exemple).

  • Chaque objet produit par évaluation ne doit pas nécessairement appartenir à la même classe (les variables locales capturées peuvent être en ligne, par exemple).

  • Si une "instance existante" est disponible, il n'a pas besoin d'être créée lors d'une précédente évaluation lambda (elle peut avoir été allouée lors de l'initialisation de la classe englobante, par exemple).

133
Holger

Lorsqu'une instance représentant le lambda est créée de manière sensible, cela dépend du contenu exact du corps de votre lambda. À savoir, le facteur clé est ce que le lambda capture à partir de l'environnement lexical. S'il ne capture aucun état variable de création en création, une instance ne sera pas créée à chaque fois que la boucle for-each est entrée. Au lieu de cela, une méthode synthétique sera générée au moment de la compilation et le site d’utilisation lambda ne recevra qu’un objet singleton qui déléguera cette méthode.

Notez en outre que cet aspect dépend de la mise en œuvre et que vous pouvez vous attendre à des améliorations et à des améliorations futures de HotSpot pour une plus grande efficacité. Il existe des plans généraux pour, par exemple, faire un objet léger sans classe correspondante complète, qui a juste assez d'informations pour être transféré à une seule méthode.

Voici un bon article accessible sur le sujet:

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

22
Marko Topolnik

Vous passez une nouvelle instance à la méthode forEach. Chaque fois que vous faites cela, vous créez un nouvel objet mais pas un pour chaque itération de boucle. L'itération est effectuée dans la méthode forEach à l'aide de la même instance d'objet 'callback' jusqu'à ce que la boucle soit exécutée.

La mémoire utilisée par la boucle ne dépend donc pas de la taille de la collection.

N'est-ce pas l'équivalent de l'extrait de code 'old syntax'?

Oui. Il y a de légères différences à un niveau très bas mais je ne pense pas que vous devriez vous en soucier. Les expressions Lamba utilisent la fonctionnalité invokedynamic à la place des classes anonymes.

0
aalku