BT

Diffuser les Connaissances et l'Innovation dans le Développement Logiciel d'Entreprise

Contribuez

Sujets

Sélectionner votre région

Accueil InfoQ Articles Révolutionner Java Avec GraalVM Native Image

Révolutionner Java Avec GraalVM Native Image

Points Clés

  • GraalVM Native Image est une technologie de compilation ahead-of-time qui génère des exécutables natifs.
  • Les exécutables natifs sont idéaux pour les conteneurs et les déploiements cloud car ils sont petits, démarrent très rapidement et nécessitent beaucoup moins de CPU et de mémoire.
  • Déployez des exécutables natifs sur des images de conteneur distroless et même Scratch pour une taille réduite et une sécurité améliorée.
  • Avec l'optimisation guidée par le profil et le ramasse-miettes G1, les exécutables natifs construits avec GraalVM Native Image peuvent atteindre un débit de pointe comparable à celui de la JVM.
  • GraalVM Native Image bénéficie d'une adoption importante avec le support des principaux frameworks Java tels que Spring Boot, Micronaut, Quarkus, Gluon Substrate, etc.

Java domine les applications d'entreprise. Mais dans le cloud, Java est plus cher que certains concurrents. La compilation native rend Java dans le cloud moins cher : elle crée des applications qui démarrent beaucoup plus rapidement et utilisent moins de mémoire.

La compilation native soulève donc de nombreuses questions pour tous les utilisateurs de Java : comment Java natif change-t-il le développement ? Quand faut-il passer au Java natif ? Quand ne devrions-nous pas ? Et quel framework devrions-nous utiliser pour Java natif ? Cette série apportera des réponses à ces questions.

Cet article d'InfoQ fait partie de la série "Les compilations natives boostent Java". Vous pouvez vous abonner pour recevoir des notifications via RSS.

GraalVM a provoqué une révolution dans le développement Java depuis son lancement il y a trois ans. L'une des fonctionnalités les plus discutées de GraalVM est Native Image, qui est basée sur une compilation ahead-of-time (AOT). Il déverrouille le profil de performances d'exécution des applications natives tout en conservant la productivité et les outils de développement familiers de l'écosystème Java.

Exécution traditionnelle d'applications Java

L'une des parties les plus puissantes et les plus intéressantes de la plate-forme Java, permettant des performances de pointe élevées, est la façon dont la machine virtuelle Java (JVM) exécute le code.

Lorsque vous exécutez votre application pour la première fois, la machine virtuelle interprète le code et collecte des informations de profilage. Malgré les performances de l'interpréteur JVM, il n'est pas aussi rapide que l'exécution de code compilé. C'est pourquoi la JVM (HotSpot) d'Oracle contient également des compilateurs just-in-time (JIT), qui compilent le code de votre application en code machine, au fur et à mesure que votre programme s'exécute. Ainsi, si votre code "préchauffe" - est fréquemment exécuté, il est compilé en code machine par le compilateur C1 du JIT. Ensuite, s'il est encore exécuté assez souvent et atteint certains seuils, il est compilé par le compilateur JIT de premier plan (C2 ou le compilateur Graal). Le compilateur de niveau supérieur effectue des optimisations basées sur les informations de profilage concernant les branches de code les plus souvent exécutées, la fréquence d'exécution des boucles et les types utilisés dans le code polymorphe.

Parfois, le compilateur effectue des optimisations spéculatives. Par exemple, la JVM peut produire une version optimisée et compilée d'une méthode basée sur les informations de profilage qu'elle collecte. Cependant, comme l'exécution du code sur la JVM est dynamique - si les hypothèses qu'elle a faites deviennent invalides ultérieurement - la JVM la désoptimisera : elle ignorera le code compilé et reviendra en mode interprété. C'est cette flexibilité qui rend la JVM si puissante : elle commence à exécuter le code rapidement, tire parti des compilateurs d'optimisation pour le code fréquemment exécuté et spécule pour appliquer des optimisations encore plus agressives.

À première vue, cette approche semble être un moyen idéal pour exécuter une application. Cependant, comme la plupart des choses, même cette approche s'accompagne de coûts et de compromis ; alors quels sont ils ici ? Lorsque la JVM effectue ses opérations (telles que la vérification du code, le chargement des classes, la compilation dynamique et la collecte des informations de profilage), elle entreprend des calculs complexes qui nécessitent un temps CPU important. En plus de ce coût, la JVM nécessite une mémoire considérable pour stocker les informations de profilage et nécessite un temps et une mémoire appréciables pour démarrer. Au fur et à mesure que de nombreuses entreprises déploient des applications dans le cloud, ces coûts deviennent plus importants car le temps de démarrage et la mémoire affectent directement le coût de déploiement d'une application. Alors, existe-t-il un moyen de réduire le temps de démarrage et l'utilisation de la mémoire tout en conservant la productivité, les bibliothèques et les outils Java que nous apprécions tous ?

La réponse est "Oui", et c'est ce que fait GraalVM Native Image.

GraalVM pour la gagne

GraalVM a commencé comme un projet de recherche chez Oracle Labs il y a 10 ans. Oracle Labs est une branche de recherche et développement d'Oracle qui étudie les langages de programmation et les machines virtuelles, l'apprentissage automatique et la sécurité, le traitement des graphes et d'autres domaines. GraalVM est un excellent exemple pour Oracle Labs - il est basé sur des années de recherche et plus de 100 articles universitaires publiés.

Au cœur même du projet se trouve le compilateur Graal - un compilateur moderne et hautement optimisé, créé à partir de zéro. Grâce à de nombreuses optimisations avancées, dans de nombreux scénarios, il génère un meilleur code que le compilateur C2. L'une de ces optimisations est l'l'analyse d'échappement partiel : elle supprime les allocations d'objets inutiles sur le tas grâce au remplacement scalaire dans les branches où l'objet n'échappe pas à l'unité de compilation, et le compilateur Graal s'assure qu'un objet existe dans le tas dans les branches où il s'échappe.

Cette approche réduit l'empreinte mémoire de l'application car moins d'objets vivent sur le tas. Cela réduit également la charge du processeur car moins de récupération de mémoire est nécessaire. De plus, les spéculations avancées dans GraalVM produisent un code machine plus rapide en tirant parti des retours d'exécution dynamiques. En spéculant que certaines parties du programme ne s'exécuteront pas pendant l'exécution, le compilateur GraalVM peut rendre le code encore plus efficace.

Vous serez peut-être surpris d'apprendre que le compilateur Graal est principalement écrit en Java. Si vous jetez un coup d'œil au référentiel GitHub principal de GraalVM, vous verrez que plus de 90 % du code y est écrit dans le langage de programmation Java, ce qui démontre une fois de plus à quel point Java est puissant et polyvalent.

Comment fonctionne Native Image

Le compilateur Graal fonctionne également comme un compilateur ahead-of-time (AOT), produisant des exécutables natifs. Étant donné la nature dynamique de Java, comment cela fonctionne-t-il exactement ?

Contrairement au mode JIT, où la compilation et l'exécution se produisent en même temps, en mode AOT, le compilateur effectue toutes les compilations pendant la construction, avant l'exécution. L'idée principale ici est de déplacer tous les "lourds" et coûteux calculs vers l'étape de construction, afin que cela puisse être fait une fois, puis au moment de l'exécution, les exécutables générés démarrent rapidement et sont prêts dès le départ parce que tout est pré-calculé et pré-compilé.

L'utilitaire GraalVM 'native-image' prend le bytecode Java en entrée et produit un exécutable natif. Pour ce faire, l'utilitaire effectue une analyse statique du bytecode sous une hypothèse de monde fermé. Lors de l'analyse, l'utilitaire recherche tout le code que votre application utilise réellement et élimine tout ce qui est inutile.

Ces trois concepts clés vous aident à mieux comprendre le processus de génération d'images natives :

  • Analyse des Points-to. GraalVM Native Image détermine quelles classes, méthodes et champs Java sont accessibles au moment de l'exécution, et seuls ceux-ci seront inclus dans l'exécutable natif. L'analyse des points d'accès commence par tous les points d'entrée, généralement la méthode principale de l'application. L'analyse traite de manière itérative tous les chemins de code accessibles de manière transitive jusqu'à ce qu'un point fixe soit atteint et que l'analyse se termine. Cela s'applique non seulement au code de l'application, mais également aux bibliothèques et aux classes du JDK - tout ce qui est nécessaire pour empaqueter une application dans un binaire autonome.
  • Initialisations au moment de la génération. GraalVM Native Image utilise par défaut l'initialisation de la classe au moment de l'exécution pour garantir un comportement correct. Mais si Native Image peut prouver que certaines classes peuvent être initialisées en toute sécurité, il les initialisera au moment de la construction à la place. Cela rend l'initialisation et les vérifications d'exécution inutiles et améliore les performances.
  • Instantané du tas (Heap snapshotting). L'instantané de tas dans Native Image est un concept très intéressant et mérite son propre article. Pendant le processus de création d'image, les objets Java alloués par les initialiseurs statiques et tous les objets accessibles sont écrits dans le tas d'image. Cela signifie que votre application démarre beaucoup plus rapidement avec un tas pré-rempli.

Ce qui est intéressant, c'est que l'analyse points-to rend les objets accessibles dans le tas de l'images, et l'instantané qui construit le tas de l'image peut rendre de nouvelles méthodes accessibles pour l'analyse points-to. Ainsi, l'analyse points-to et l'instantané du tas sont effectués de manière itérative jusqu'à ce qu'un point fixe soit atteint :

Processus de création d'image native

Une fois l'analyse terminée, Graal compile tout le code accessible dans un exécutable natif spécifique à la plate-forme. Cet exécutable est entièrement fonctionnel et n'a pas besoin de la JVM pour s'exécuter. En conséquence, vous obtenez une version exécutable native léger et rapide de votre application Java : une version qui exécute exactement les mêmes fonctions mais ne contient que le code nécessaire et ses dépendances requises.

Mais qui s'occupe des fonctionnalités telles que la gestion de la mémoire et la planification des threads dans l'exécutable natif ? Pour cela, Native Image inclut Substrate VM - une implémentation de VM légère qui fournit des composants d'exécution, tels qu'un ramasse-miettes et un planificateur de threads. Tout comme le compilateur Graal, Substrate VM est écrit dans le langage de programmation Java et compilé par AOT par GraalVM Native Image en code natif !

Grâce à la compilation AOT et à l'instantané du tas, Native Image permet un tout nouveau profil de performances pour vos applications Java. Examinons cela de plus près.

Faire passer les performances de démarrage Java au niveau supérieur

Vous avez peut-être entendu dire qu'un exécutable généré par Native Image a d'excellentes performances de démarrage. Mais qu'est-ce que cela signifie exactement ?

Démarrage instantané. Contrairement à l'exécution sur la JVM, où le code est d'abord vérifié, interprété, puis (après préchauffage) finalement compilé, un exécutable natif est livré avec un code machine optimisé dès le départ. Un autre terme que j'aime utiliser est la performance instantanée - une application est prête à effectuer un travail significatif dans ses premières millisecondes d'exécution, sans aucune surcharge de profilage ou de compilation.

 

JIT AOT
  • Le système d'exploitation charge l'exécutable de la JVM
  • La machine virtuelle charge les classes à partir du système de fichiers
  • Le bytecode est vérifié
  • L'interprétation du bytecode démarre
  • Exécution des initialiseurs statiques
  • Compilation de premier niveau (C1)
  • Métriques de profilage recueillies
  • ... (après un certain temps)
  • Compilation de second niveau (compilateur C2/ Graal)
  • Enfin fonctionnant avec un code machine optimisé
  • Le système d'exploitation charge l'exécutable avec un tas préparé
  • L'application démarre immédiatement avec un code machine optimisé

 

Effet sur le temps de démarrage des modes JIT et Image native

Efficacité de la mémoire. Un exécutable natif ne nécessite ni la JVM et son infrastructure de compilation JIT ni de mémoire pour le code compilé, les données de profil et les caches de bytecode. Tout ce dont il a besoin est de la mémoire pour l'exécutable et les données de l'application. Voici un exemple :

Utilisation de la mémoire et du processeur en modes JIT et Image native

Les graphiques ci-dessus montrent le comportement d'exécution d'un serveur Web sur une JVM (à gauche) et en tant qu'exécutable natif (à droite). La ligne turquoise indique la quantité de mémoire utilisée : 200 Mo en mode JIT contre 40 Mo pour l'exécutable natif. Les lignes rouges indiquent l'activité du processeur : la JVM utilise fortement le processeur pendant les activités JIT de préchauffage décrites précédemment, tandis que l'exécutable natif utilise à peine le processeur, car toutes les opérations de compilation coûteuses ont eu lieu au moment de la génération. Un tel comportement d'exécution rapide et économe en ressources fait de Native Image un excellent modèle de déploiement où l'utilisation de moins de ressources pendant moins de temps réduit considérablement les coûts (microservices, serverless et charges de travail cloud en général).

Taille du packaging. Un exécutable natif ne contient que le code requis. C'est pourquoi il est beaucoup plus petit que la taille combinée du code d'application, des bibliothèques et d'une JVM. Dans certains scénarios, tels que le travail dans des environnements à ressources limitées, la taille de compression de votre application peut être importante. Des utilitaires tels qu'UPX compressent encore plus les exécutables natifs.

Performances de pointe comparables à la JVM

Qu'en est-il des performances de pointe, cependant ? Comment Native Image optimise-t-elle le débit de pointe au moment de l'exécution lorsque tout est compilé à l'avance ?

Nous nous efforçons de faire en sorte que Native Image offre d'excellentes performances de pointe ainsi qu'un démarrage rapide. Il existe déjà plusieurs façons d'améliorer les performances maximales des exécutables natifs :

  • Optimisations guidées par le profil. Étant donné que Native Image optimise et compile le code à l'avance, par défaut, il n'a pas accès aux informations de profilage d'exécution pour optimiser le code lorsque l'application s'exécute. Une façon de résoudre ce problème consiste à utiliser l'optimisation guidée par profil (profile-guided optimization ou PGO). Avec PGO, les développeurs peuvent exécuter une application, collecter les informations de profilage, puis les réinjecter dans le processus de génération d'images natives. L'utilitaire 'native-image' utilise ces informations pour optimiser les performances de l'exécutable résultant en fonction du comportement d'exécution de votre application. PGO est disponible dans GraalVM Enterprise, qui est une version commerciale de GraalVM, fournie par Oracle.
  • Gestion de la mémoire dans Native Image. Le ramasse-miettes par défaut dans un exécutable généré par Native Image est Serial GC, ce qui est optimal pour les microservices avec un petit tas. Des options GC supplémentaires sont également disponibles :
    • La ramasse-miettes Serial dispose désormais d'une nouvelle politique permettant aux espaces survivor de la young generation de réduire l'encombrement de la mémoire d'exécution des applications. Depuis l'introduction de cette politique, nous avons mesuré jusqu'à 23,22 % d'améliorations du débit de pointe pour une charge de travail de microservices typique telle que Spring Petclinic.
    • Vous pouvez également utiliser le ramasse-miettes G1 à faible latence pour un débit encore meilleur (disponible dans GraalVM Enterprise). G1 convient mieux aux grands tas.

Avec PGO et le ramasse-miettes G1, les exécutables natifs atteignent des performances optimales comparables à la JVM :

Moyenne géométrique des benchmarks Renaissance et DaCapo

Avec ces options, vous pouvez optimiser chaque dimension de performance de votre application avec Native Image : temps de démarrage, efficacité de la mémoire et débit de pointe.

Réflexion, configuration et autres mythes autour de Native Image

Étant donné que Native Image est une toute nouvelle façon d'exécuter des applications Java, il y a quelques points à garder à l'esprit.

Vous avez peut-être entendu dire que GraalVM Native Image ne prend pas en charge la réflexion. Ce n'est pas vrai.

Native Image effectue une analyse statique sous l'hypothèse d'un monde fermé. Par conséquent, les fonctionnalités Java dynamiques, telles que la réflexion, nécessitent une configuration supplémentaire pour que le processus de génération réussisse. Lorsqu'il effectue une analyse statique de votre application Java, Native Image essaie de détecter et de gérer les appels à l'API Reflection. Cependant, en général, cette analyse automatique n'est pas toujours suffisante, et les éléments de programme accessibles de manière réflexive lors de l'exécution devraient être spécifiés via la configuration. Vous pouvez créer cette configuration manuellement ou tirer parti de l'agent de traçage d'image native. L'agent suit l'utilisation des fonctionnalités dynamiques pendant l'exécution du programme sur la JVM et produit un fichier de configuration. Ce fichier est utilisé par l'utilitaire Native Image pour inclure des parties d'un programme accessible par réflexion. Bien que l'agent soit utile pour obtenir la configuration initiale, nous vous recommandons de l'inspecter manuellement et de le compléter si nécessaire.

Une configuration similaire peut être requise lors de l'utilisation de Java Native Interface (JNI), d'objets Dynamic Proxy et de ressources du classpath. Vous pouvez également utiliser le même agent de traçage pour configurer l'utilisation de toutes ces fonctionnalités.

Enfin, vous pouvez utiliser le GraalVM Dashboard, une application Web qui visualise la compilation d'images natives, pour découvrir quels packages, classes et méthodes ont été inclus dans l'exécutable natif, et pour identifier également quels objets occupent le plus d'espace dans le tas.

Changer le jeu dans le cloud avec Java

Native Image fait une énorme différence pour les déploiements cloud, où elle peut avoir un impact important sur le profil de consommation des ressources de vos applications. Nous avons déjà appris que les exécutables natifs produits par Native Image démarrent rapidement et nécessitent moins de mémoire. Qu'est-ce que cela signifie exactement pour les déploiements dans le cloud, et comment GraalVM peut-il vous aider à minimiser vos images de conteneur Java ?

Comme nous l'avons déjà établi, les applications générées par Native Image n'ont pas besoin de la JVM pour s'exécuter : elles peuvent être autonomes et inclure tout ce dont votre application a besoin pour s'exécuter. Cela signifie que vous pouvez mettre votre application dans une image Docker légère, et elle sera entièrement fonctionnelle par elle-même. La taille de l'image dépendra de ce que fait votre application et des dépendances qu'elle inclut. L'application basique "Hello, World!", construite avec un framework de microservices Java, est d'environ 20 Mo.

Avec Native Image, vous pouvez également créer des exécutables statiques et principalement statiques. Un exécutable natif principalement statique est lié de manière statique à toutes les bibliothèques, à l'exception de 'libc', qui est fournie par l'image du conteneur. Vous pouvez utiliser une image de conteneur dite distroless pour les déploiements légers. Les images Distroless n'incluent que des bibliothèques pour exécuter l'application et n'ont pas de shells, de gestionnaires de packages et d'autres programmes. Par exemple, votre Dockerfile pourrait simplement être :

```
FROM gcr.io/distroless/base
COPY build/native-image/application app
ENTRYPOINT ["/app"]
```

Pour un déploiement complètement autonome qui ne nécessite même pas l'image du conteneur pour fournir la libc, vous pouvez lier statiquement votre application avec 'musl-libc '. Vous pouvez le mettre dans une image Docker "FROM scratch" car il est entièrement autonome.

Utilisation de Native Image en production

Jusqu'à présent, nous avons expliqué comment optimiser les performances de l'application que vous avez générée à l'aide de Native Image et envisagé quelques hacks utiles que vous pouvez appliquer pendant le processus de construction. Maintenant, y a-t-il autre chose que vous puissiez faire pour tirer le meilleur parti de vos applications ? Oui beaucoup.

Pour simplifier la création, le test et l'exécution d'une application Java en tant qu'exécutable natif, utilisez les plugins officiels Maven et Gradle fournis par l'équipe GraalVM. De plus, ces plugins prennent en charge les tests JUnit 5 natifs. Ils ont été développés en collaboration avec les équipes JUnit, Micronaut et Spring et démontrent un excellent exemple de collaboration dans l'écosystème JVM.

Pour configurer GraalVM Native Image dans vos flux de travail GitHub Action, utilisez GitHub action for GraalVM. L'action configurable prend en charge plusieurs versions de GraalVM et versions pour développeurs et configure entièrement GraalVM et des composants spécifiques.

Parlons un peu de l'outillage. Lors du développement d'une application Java que vous souhaitez distribuer en tant qu'exécutable, vous pouvez utiliser les mêmes outils que vous utiliseriez normalement. Vous pouvez utiliser n'importe quel IDE et n'importe quel JDK, y compris le JDK GraalVM, pour créer, tester et déboguer votre application, puis utiliser l'utilitaire GraalVM Native Image pour effectuer la dernière étape de compilation native. Selon la complexité de l'application, la compilation par NativeImage peut prendre un certain temps, il est donc logique de l'effectuer en dernière étape. Cependant, nous travaillons sur un mode de développement rapide pour Native Image qui réduira considérablement le temps de compilation en n'effectuant pas la plupart des optimisations nécessaires au déploiement en production.

Même si vous pouvez développer votre application sur la JVM, puis créer un exécutable natif plus tard dans votre processus de développement, nous avons reçu de nombreuses demandes de notre communauté pour améliorer les temps de construction et l'utilisation des ressources. Nous avons beaucoup travaillé sur ce problème au cours des deux dernières versions. Avec la dernière version de GraalVM (22.0), vous pouvez produire un exécutable natif à partir d'une application Java hello-world en environ 13,8 secondes, et la taille de l'exécutable sera d'environ 5 Mo. Nous avons également réduit l'utilisation de la mémoire d'environ 10 %.

Pour déboguer un exécutable construit à l'aide de Native Image, vous pouvez soit utiliser 'gdb' depuis la ligne de commande (sur Linux et macOS), soit les extensions VS Code de GraalVM. Ce tutoriel fournit des instructions étape par étape.

Pour surveiller les performances de votre exécutable natif, utilisez le JDK Flight Recorder. La prise en charge complète de Native Image est toujours en cours, mais vous pouvez déjà utiliser pour observer les événements personnalisés et système.

Pour une surveillance supplémentaire des performances, générez un heap dump d'un exécutable natif puis analysez-le à l'aide d'un outil tel que VisualVM. Il s'agit d'une fonctionnalité de GraalVM Enterprise.

Adopté par les frameworks Java

Il serait très difficile d'écrire des applications de qualité industrielle sans la prise en charge de frameworks Java. Heureusement, vous n'êtes pas obligé. Tous les principaux frameworks prennent en charge Native Image (répertoriés par ordre alphabétique) : Gluon Substrate, Helidon, Micronaut, Quarkus et Spring Boot. Tous ces frameworks exploitent GraalVM Native Image pour améliorer considérablement les temps de démarrage et l'utilisation des ressources des applications, ce qui les rend parfaits pour des déploiements cloud efficaces. Les prochains articles de cette série décriront comment les frameworks utilisent GraalVM Native Image.

L'avenir de Native Image

Depuis sa première sortie publique, Native Image a fait d'énormes progrès. Il est largement adopté par les frameworks Java, les fournisseurs de cloud proposent Native Image en tant que runtime Java et de nombreuses bibliothèques fonctionnent avec Native Image prête à l'emploi. Nous avons apporté plusieurs modifications à l'expérience des développeurs et, comme le montre notre étude de l'année dernière, 70 % des développeurs qui utilisent déjà GraalVM l'utilisent pour créer et distribuer des exécutables natifs.

Nous avons de nombreuses idées pour de nouvelles fonctionnalités et améliorations dans Native Image, notamment :

  • Prise en charge d'un plus grand nombre de plates-formes
  • Simplification de la configuration et de la compatibilité des bibliothèques Java
  • Poursuivre l'amélioration des performances optimales
  • Continuer à travailler avec les équipes de frameworkw Java pour tirer parti de toutes les fonctionnalités de Native Image, en développer de nouvelles, améliorer les performances et garantir une excellente expérience de développement
  • Introduction d'un mode de compilation de développement plus rapide
  • Prise en charge des threads virtuels du projet Loom
  • Prise en charge des IDE pour la configuration de Native Image et la configuration basée sur l'agent
  • Poursuite de l'amélioration des performances du ramasse-miettes et ajout de nouvelles implémentations de ramasse-miettes

Nous sommes reconnaissants à la communauté et à nos partenaires de nous aider à faire avancer Native Image et à le rendre de plus en plus utile pour chaque développeur Java. S'il y a de nouvelles fonctionnalités ou améliorations que vous souhaitez voir dans Native Image, partagez vos commentaires avec nous via les plates-formes communautaires de GraalVM !

Java domine les applications d'entreprise. Mais dans le cloud, Java est plus cher que certains concurrents. La compilation native rend Java dans le cloud moins cher : elle crée des applications qui démarrent beaucoup plus rapidement et utilisent moins de mémoire.

La compilation native soulève donc de nombreuses questions pour tous les utilisateurs de Java : comment Java natif change-t-il le développement ? Quand faut-il passer au Java natif ? Quand ne devrions-nous pas ? Et quel framework devrions-nous utiliser pour Java natif ? Cette série apportera des réponses à ces questions.

Cet article d'InfoQ fait partie de la série "Les compilations natives boostent Java". Vous pouvez vous abonner pour recevoir des notifications via RSS.

 

Au sujet de l’Auteur

Evaluer cet article

Pertinence
Style

Contenu Éducatif

BT