La saison 6 du Paris JUG recommence, avec 9 soirées prévues pour 2013-2014. Nouvelle équipe, avec 4 nouveaux volontaires, pour venir aider à organiser et à animer les soirées. Merci à Audrey Neveu, Guillaume Duquesnay, Guillaume Dufour et Khanh Tuong Maudoux. A titre perso, je passe la main, après plusieurs années passées à participer et à organiser les soirées du JUG. Je continue à organiser Devoxx France avec Antonio, José et Zouheir.
Ceci étant dit, allons-y pour la première soirée.
Au programme ce soir, Rémi Forax. Maître de conférence à l’Université Paris-Est de Marne-la-vallée (UPEM). Rémi fait parti de l’expert group pour le JCP sur la JSR 292 (invokedynamic) et la JSR 335 (lamdda). La JSR 292 n’est pas fermée et continue à mobiliser des équipes. Ceci permet de garder les créateurs des JVM autour de la table. La JSR 335 regroupe autant le langage que les APIs. Rémi code au niveau du projet OpenJDK, du projet ASM. Il a aussi codé son propre compilateur de compilateur, Tatoo.
Historiquement, un livre était suffisant pour apprendre Java. On ne lisait que Java in a Nutshell et on était développeur Java. Ça, c’était en 1996//1997. Tout était magique, le cross-platform, la possibilité de faire des Applets, de l’AWT et des applications serveurs simples. Au fil du temps, les choses se sont compliquées.
Le sujet de ce soir, c’est Java EE, l’hyper-utilisation des Proxy en Java, pourquoi ce n’est pas optimal, et donc que pouvons-nous faire avec invokedynamic pour améliorer cela ?
L’un des soucis identifiés par Rémi Forax sur les implémentations de Java EE, c’est l’empilement de couche. On essaye alors de simplifier, de mettre des profils, mais il y a encore de la complexité. Il y a d’abord un problème technique. Les Implémentations de JavaEE font que l’on a cette façon de développer. On peut simplifier le développement en simplifiant le coeur. On pourra revenir sur du plus simple. Pour l’instant, pour Remi, Java EE c’est Evil Edition.
En travaillant sur invokedynamic, les membres de la JSR 292 ont rencontré beaucoup de développeurs de VM. Une implémentation de Java EE ressemble en fait beaucoup à un langage dynamique. On peut donc donner la meme approche à Java EE, que ce que l’on a fait avec invokedynamic. On peut très bien voir Java EE comme une spécification modulaire. La spec est modulaire au départ. La façon de lier le code et les annotations n’est cependant pas ouverte. C’est spécifié, mais c’est spécifique à une implémentation. Donc si on souhaite ajouter son annotation, c’est compliqué à faire. On se retrouve avec un JBoss Security et un Spring Security. Certains trucs simples sont trop ré-implémentés.
Rémi Forax présente ensuite un schéma de l’architecture de Java EE 7. Plus de 32 JSR, 8000 pages de spécifications selon Antonio Goncalves qui en parlait dans l’épisode 85 des CastCodeurs. La question est donc, comment relier une annotation à son implémentation ? Finalement, c’est une forme d’injection de dépendances. Le principe des intercepteurs, c’est la même chose.
En général, dans notre code métier annoté avec par exemple @Transactional, cela va se passer avec des Proxy Java.
Pour créer un Proxy pour une interface Foo, voici comment faire :
InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler });
Plus simplement, on peut aussi écrire ceci:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
Lorsqu’un appel est effectué sur la fonction de votre code métier, il va passer par un proxy, puis un gestionnaire de transaction, puis d’éventuels intercepteurs, pour enfin que la méthode métier soit appelée.
C’est chouette, mais lorsqu’une exception est levée, on se retrouve alors avec une méga stacktrace Java.
Par ailleurs, et c’est là le point le plus important, nous allons nous apercevoir que les Proxy empêchent la JVM d’optimiser et d’inliner le code. Rémi enchaine alors une démonstration avec un code simple, en allant jusqu’à nous montrer la stack et les appels. En 10mn, nous nous rendons compte qu’en effet, les Proxy ne sont pas optimisés par la JVM. Si vous appelez ensuite une base de données, ceci ne se verra sans doute pas. Mais le coût et la perte d’efficacité, le fait que les Proxy soient utilisés partout dans les implémentations de la spécification Java EE… c’est dommage.
Si vous voulez regarder ce que fait la JVM, vous pouvez passer quelques options à votre JVM :
- -XX:+PrintCompilation (voir cet article très complet, scrollez en bas de la page)
- PrintInlining pour voir les méthodes inlinées. Si telle méthode appelle tel impl, alors on ramene l’impl dans la méthode. Les if-null qui ne servent pas.
- LogCompilation (sort toutes les décisions de la JVM)
- PrintAssembly (diagnostic, hsdis hotspot dissasembler)
Après avoir expliqué les différentes options, nous le suivons dans les arcanes de la JVM, et du code compilé. La conclusion est que lorsque vous utilisez des Proxy, il n’y a pas d’inline entre le site de départ et le site d’arrivée.
La VM n’aime donc pas les proxies car cela va l’obliger à
- boxer les arguments
- utiliser la reflection pour appeler la méthode cible
Invokedynamic comme solution
Après la pause buffet, nous reprenons avec une démonstration et une solution basée sur invokedynamic. Rémi Forax raconte l’histoire de la JSR 292 qui a presque 5 ans. C’est aussi une aventure humaine, où un groupe de passionné a pu avancer, alors qu’Oracle rachetait SUN, et donc vraiment faire avancer les performances de la JVM. Aujourd’hui, la JVM optimise mieux les langages dynamiques comme Groovy ou JRuby, que Java. On peut même imaginer avec une application Rails avec JRuby, qui tournerait mieux qu’une application web (cough… cough…) écrite avec les spécifications de Java EE…
L’appel de méthode pour les langages typés dynamiquement, en fait c’est simple. Invokedynamic introduit un meta protocole, qui permet d’aider la JVM et donc de booster les performances… Si cela marche avec les langages typés dynamiquement, cela devrait aussi marcher avec Java EE.
A propos, Ruby est un langage typé dynamiquement et avec un typage fort.
// won't work a = "40" b = a + 10
Ici, 10 ne sera pas convertit en une String, 10 étant un Fixnum (voir cet article)
Dans les implémentations de certaines spécifications Java EE, cela ressemble finalement aux appels effectués avec invokedynamic. Lorsque l’on souhaite effectuer un appel, on veut dire à la VM où l’on souhaite arriver ce qui permettra à celle-ci d’optimiser ensuite le code, et donc d’avoir de meilleurs performances.
Je vous invite à lire « Java Virtual Machine Support for non-java language » pour comprendre invokedynamic et sa mise en oeuvre. Rémi a couvert un exemple complet, avec bootstrap, methodHandler, mais je ne pourrai pas vous retranscrire cette partie.
Conclusion et 3eme mi-temps
Pour conclure donc, il est possible d’améliorer les performances de Java EE, de se passer de l’empilement de couches, et de revenir sur des stacks d’erreur simples. Cette approche introduit une notion de modularité, où plusieurs Container pourraient fournir des implémentations. Il y a un travail à initier pour la prochaine version de Java EE.
D’un point de vue personnel, si j’ai trouvé la présentation intéressante, je regrette cependant que l’on soit aussi loin des préoccupations des développeurs métiers. Tout d’abord, avons-nous besoin de toutes ces spécifications ? Ensuite, est-ce que le conteneur, le fameux serveur d’application Java EE, a encore une raison d’exister ? En général, nous n’utilisons qu’une partie de la spécifications. Un peu de JPA et du JAX-RS. Ou une servlet avec un peu de JSP. Mais la vision « Etoile de la mort » où une méga patate est capable de tout faire (Tx, Security, MBeans, EJB, le café et l’addition) a du plomb dans l’aile. La difficulté de l’idée de Rémi, c’est qu’il s’attaque à l’implémentation, pas à la spécification. C’est la façon dont les sociétés et les projets open-sources mettent en oeuvre les specs qu’il faut revoir.
Ensuite moi je m’interroge lorsque je vois une classe Java simple, où maintenant 30% du fichier est remplis par des annotations. J’ai posé la question à Rémi. Tous les langages ont des systèmes de méta-annotation. Et c’est une force pour Java, que de pouvoir se reposer sur un système… qui au départ servait à faire de la javadoc. Bon, je sais pas. Mais peut-être qu’un jour quelqu’un dira que finalement, c’était ridicule en 2013.
Autre point que j’ai pu abordé avec lui, c’est son avis sur Scala. Il y a selon Rémi plusieurs points intéressants. Et là, on discute avec un gars qui connaît la JVM. Le premier souci de Scala selon Rémi, c’est d’abord qu’il existe différentes façons, peut-être trop de façons, d’écrire la même chose. Un développeur Scala pragmatique n’aura pas la même façon d’écrire qu’un débutant, ou qu’un développeur Java. Lorsqu’il explique cela, je pense à l’accent que l’on peut avoir lorsque l’on parle une langue étrangère. Ah… Nicolas qui parle Anglais… ce mec est Français je pense. A cela, on peut répondre que Java aussi, au départ, souffrait d’un manque de cohésion. Qui n’a jamais codé en Français ses classes Java ? Qui n’a jamais utilisé l’accolade à la ligne ? Qui continue à faire des IMachin et des Machins ? Ou des Machins Interface et des MachinsImpl ?
Deuxième point sur lequel Rémi est critique concernant le langage, c’est le nombre de features. En général, et c’est le succès de Java, les créateurs essayent de retirer et de faire faire par le langage ce que vous aviez à faire vous même auparavant. Java est donc un langage qui tourne sur des systèmes d’exploitations différents (write once, run everywhere) et qui se charge de l’allocation et de la gestion de la mémoire (garbage collector). Je pense très sincérement que Java est un meilleur langage que le C, car il ne laisse pas le développeur faire sa gestion mémoire, il est capable d’optimiser le code et donc de tourner plus vite que du C, et enfin, il permet de coder sur un OS pour envoyer en prod sur un autre OS.
Scala, d’après Rémi, n’a pas cette caractéristique, d’avoir apporté un gros changement dans le développement comme Java l’avait fait. Il permet cependant d’écrire et de résoudre un problème avec une autre approche. Le mélange du paradigme Objet et du Fonctionnel, oui ça, c’est du plus. Mais qu’y-a-t-il de moins que ce que nous faisions en Java ? A cela, je lui répond que Scala (pour Scalable) est un langage pensé pour être optimisé et fonctionner sur des architectures multi-coeurs. Les possibilités de traitement en parrallèle, l’approche avec l’immutabilité, et donc le paradigme fonctionel, sont pour moi les caractéristiques intéressantes du langage.
Autre point, selon lui, et je pense qu’il a raison en partie, les Lambdas dans Java 8 devraient apporter à Java ce petit plus, qui permettra de combler le retard du langage par rapport à Scala. L’avenir de Scala n’est donc pas assuré comme langage principal, mais comme un langage secondaire à suivre. Sur ce point, je pense aussi que Java restera le langage majoritairement utilisé pour les 10 ans qui viennent. Ajoutez Javascript, et vous avez une idée de ce que vous devez regarder.
Enfin, selon lui, l’équipe Scala sait écrire des compilateurs. Mais elle ne connaît pas la JVM. Et ça, c’est un souci pour le langage et ses performances. Son point de vue est qu’il est nécessaire de connaître la JVM pour bien écrire un langage.
Pour pondérer ce point, j’ai regardé, et je pense que Rémi n’a pas tout à fait raison. Tout d’abord parce que des personnes de la communauté Scala étaient au fameux JVM Summit 2013. Ensuite parce que Martin Odersky, l’auteur du compilateur javac depuis 1.4, doit aussi connaître la JVM. Enfin parce que quelques minutes de recherche sur le web m’ont montré que l’EPFL était plutôt au fait du fonctionnement de la JVM. Je veux bien développer cela dans un autre billet, car le sujet est important. Si vous pensez que les contributeurs de Scala ne connaissent pas la JVM, merci d’argumenter car cette question est intéressante
http://stackoverflow.com/questions/14285894/advantages-of-scala-emitting-bytecode-for-the-jvm-1-7
http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q4/2012-11-04-FasterClosures.pdf
http://grokbase.com/t/gg/scala-internals/12be82bdk8/impasse-in-poor-mans-method-handles-due-to-lambdalift-behavior
http://thread.gmane.org/gmane.comp.lang.scala.internals/15435
http://thread.gmane.org/gmane.comp.lang.scala.internals/9397
https://groups.google.com/forum/#!topic/scala-internals/RBPzV6oJXzM%5B1-25-false%5D
En conclusion, un moment sympa, avec un vrai passionné. On ne ressort pas neutre d’une présentation, où l’on se prend à imaginer un Java EE dynamics. Je crois plus à l’utilisation de quelques spécifications Java EE. Mais plus tellement à la vision « boite en carton posée sur l’étagère avec 10 DVD ». Ensuite, même si sa démonstration technique reste intéressante, ce qu’il a montré concerne l’implémentation d’une spécification.
Concernant Java 9, il a évoqué le possible retour de Jigsaw et de la modularité. Si la gouvernance de Java EE est plutôt partagée, il faut comprendre que Java SE est plutôt dans les mains d’Oracle, dans les mains de l’architecte en chef de la plateforme Java, Mark Reinhold.
Bref… je retourne à mon code Scala, avec du Javascript, du Java et du Groovy.
Etre polyglotte permet de ne pas s’enfermer dans une certaine vision de notre métier.
Nicolas, il s’agit de ‘invokedynamic’, pas ‘invoke dynamics’ 🙂
Corrigé, merci !
Le premier souci de Scala selon Rémi, c’est d’abord qu’il existe différentes façons, peut-être trop de façons, d’écrire la même chose.
Mais encore ? J’attends toujours des exemples pertinants pour cette remarque (que je n’avais pas entendue depuis deux ans au moins), qui ne soit pas transposable à Java… En général, une personne disant ça ne se rend pas compte en quoi deux solutions a priori identiques ne sont en réalité pas du tout equivalentes. Il en est de même pour tout langage mis dans les mains d’un non-expert…
Mais qu’y-a-t-il de moins que ce que nous faisions en Java ?
Le jour où Java propose (au moins et par ordre d’importance) l’inférence de type, les typeclasses, une API potable pour les collections et l’équivalent de
object
, alors on en reparle 🙂Dans une autre conversation (et je suppose que c’est qu’il voulait dire)Rémi disait que cela rend le langage trop complexe/difficile à apprendre.
Réduisant le nombre de développeurs potentiel et donc l’intérêt pour une boîte de l’utiliser (Le problème étant de trouver la main d’œuvre qualifier).
« A cela, je lui répond que Scala (pour Scalable) est un langage pensé pour être optimisé et fonctionner sur des architectures multi-coeurs. Les possibilités de traitement en parrallèle, l’approche avec l’immutabilité, et donc le paradigme fonctionel, sont pour moi les caractéristiques intéressantes du langage. »
Clairement oui, d’ailleurs la plupart des projets (Spark, Play…) ou des boites (Twitter, Linkedin…) qui ont choisi Scala pour langage d’implémentation reposent beaucoup (et avec succès) sur ces aspects là.
En java les lambdas amélioreront un peu les choses mais ça restera quand même moins naturel qu’en Scala, et l’immutabilité plus difficilement applicable sur tout le code…