web-dev-qa-db-fra.com

Optimisation des performances JVM pour les grandes applications

Les paramètres JVM par défaut ne sont pas optimaux pour exécuter de grandes applications. Toutes les idées de personnes qui l'ont réglé sur une application réelle seraient utiles. Nous exécutons l'application sur une machine Windows 32 bits, où la machine virtuelle Java cliente est utilisée par défaut . Nous avons ajouté -server et changé le NewRatio en 1: 3 (une jeune génération plus importante).

D'autres paramètres/réglages que vous avez essayés et trouvés utiles?

[Mise à jour] Le type spécifique d'application dont je parle est une application serveur qui est rarement arrêtée, prenant au moins -Xmx1024m. Supposons également que l'application est déjà profilée. Je recherche des directives générales en termes de performances JVM uniquement.

31
amit

Il existe de grandes quantités de ces informations.

Tout d'abord, profilez le code avant de régler la JVM.

Deuxièmement, lisez attentivement la documentation JVM ; il y a beaucoup de "légendes urbaines" autour. Par exemple, l'indicateur -server n'est utile que si la machine virtuelle Java reste résidente et fonctionne pendant un certain temps; -server "met en place" le JIT/HotSpot, et cela doit avoir plusieurs passages sur le même chemin pour être activé. -server, d'autre part, ralentit l'exécution initiale de la JVM, car il y a plus de temps de configuration.

Il existe plusieurs bons livres et sites Web. Voir, par exemple, http://www.javaperformancetuning.com/

18
Charlie Martin

Préface

Contexte

Été dans une boutique Java. J'ai passé des mois entiers à exécuter des tests de performances sur des systèmes distribués, les principales applications étant en Java. Certains d'entre eux impliquant des produits développés et vendus par Sun eux-mêmes (puis Oracle).

Je vais passer en revue les leçons que j'ai apprises, un peu d'histoire sur la JVM, quelques discussions sur les internes, quelques paramètres expliqués et enfin quelques réglages. Essayer de le garder au point afin que vous puissiez l'appliquer dans la pratique.

Les choses changent rapidement dans le monde Java donc une partie de celui-ci pourrait être déjà dépassé depuis l'année dernière, j'ai fait tout cela. (Is Java 10 out déjà?)

Bonnes pratiques

Ce que vous DEVEZ faire: benchmark, Benchmark, BENCHMARK!

Lorsque vous avez vraiment besoin de connaître les performances, vous devez effectuer de véritables benchmarks, spécifiques à votre charge de travail. Il n'y a pas d'alternative.

De plus, vous devez surveiller la JVM. Activer la surveillance. Les bonnes applications fournissent généralement une page Web de surveillance et/ou une API. Sinon, il existe le Java Java (JVisualVM, JMX, hprof et certains drapeaux JVM).

Sachez qu'il n'y a généralement aucune performance à gagner en réglant la JVM . C'est plus un "planter ou ne pas planter, trouver le point de transition". Il s'agit de savoir que lorsque vous donnez que quantité de ressources à votre application, vous pouvez toujours vous attendre à que quantité de performances en retour. La connaissance, c'est le pouvoir.

Les performances sont principalement dictées par votre application. Si vous voulez plus vite, vous devez écrire un meilleur code.

Ce que vous FEREZ la plupart du temps: Vivez avec des valeurs par défaut sensibles et fiables

Nous n'avons pas le temps d'optimiser et de régler chaque application. La plupart du temps, nous vivons simplement avec des valeurs par défaut raisonnables.

La première chose à faire lors de la configuration d'une nouvelle application est de lire la documentation. La plupart des applications sérieuses sont accompagnées d'un guide pour l'optimisation des performances, y compris des conseils sur les paramètres JVM.

Ensuite, vous pouvez configurer l'application: Java_OPTS: -server -Xms???g -Xmx???g

  • -server: activer les optimisations complètes (ce drapeau est automatique sur la plupart des JVM de nos jours)
  • -Xms-Xmx: définit le tas minimum et maximum (toujours la même valeur pour les deux, c'est à peu près les seules optimisations à faire).

Bravo, vous connaissez tous les paramètres d'optimisation qu'il y a à savoir sur la JVM, félicitations! C'était simple: D

CE QUE VOUS NE DEVEZ PAS FAIRE, JAMAIS:

Veuillez NE PAS copier la chaîne aléatoire que vous avez trouvée sur Internet, surtout quand ils prennent plusieurs lignes comme ça:

-server  -Xms1g -Xmx1g  -XX:PermSize=1g -XX:MaxPermSize=256m  -Xmn256m -Xss64k  -XX:SurvivorRatio=30  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark  -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof   -Dcom.Sun.management.jmxremote.port=5616 -Dcom.Sun.management.jmxremote.authenticate=false -Dcom.Sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

Par exemple, cette chose trouvée sur la première page de Google est tout simplement terrible. Il existe des arguments spécifiés plusieurs fois avec des valeurs contradictoires. Certains forcent simplement les valeurs par défaut de la JVM (éventuellement les valeurs par défaut des 2 versions JVM antérieures). Certains sont obsolètes et simplement ignorés. Et enfin, au moins un paramètre est si invalide qu'il plantera systématiquement la JVM au démarrage par sa simple existence.

Réglage réel

Comment choisissez-vous la taille de la mémoire:

Lisez le guide de votre candidature, il devrait donner quelques indications. Surveillez la production et ajustez ensuite. Effectuez des tests de performance si vous avez besoin de précision.

Remarque importante : Le processus Java prendra jusqu'à tas max PLUS 10 % . La surcharge X% étant la gestion du tas, non incluse dans le tas lui-même.

Toute la mémoire est généralement préallouée par le processus au démarrage. Vous pouvez voir le processus en utilisant le tas max tout le temps. Ce n'est tout simplement pas vrai. Vous devez utiliser Java pour voir ce qui est réellement utilisé.

Trouver la bonne taille:

  • S'il se bloque avec OutOfMemoryException, ce n'est pas assez de mémoire
  • S'il ne plante pas avec OutOfMemoryException, c'est trop de mémoire
  • Si c'est trop de mémoire MAIS le matériel l'a et/ou est déjà payé, c'est le numéro parfait, le travail est fait!

JVM6 est en bronze, JVM7 est en or, JVM8 est en platine ...

La JVM s'améliore pour toujours. La collecte des ordures est une chose très complexe et il y a beaucoup de gens très intelligents qui y travaillent. Il a connu d'énormes améliorations au cours de la dernière décennie et il continuera de le faire.

À des fins d'information. Ce sont au moins 4 Garbage Collectors disponibles dans Oracle Java 7-8 (HotSpot) et OpenJDK 7-8. (Les autres JVM peuvent être entièrement différentes, par exemple Android, IBM, embarquées):

  • SerialGC
  • ParallelGC
  • ConcurrentMarkSweepGC
  • G1GC
  • (plus variantes et paramètres)

[À partir de Java 7 et plus. Le code Oracle et OpenJDK sont partiellement partagés. Le GC devrait être (principalement) le même sur les deux plates-formes.]

JVM> = 7 a de nombreuses optimisations et choisit des valeurs par défaut décentes. Cela change un peu par plateforme. Il équilibre plusieurs choses. Par exemple, décider d'activer ou non les optimisations multicœurs si le processeur a plusieurs cœurs. Vous devriez le laisser faire. Ne modifiez pas et ne forcez pas les paramètres GC.

Vous pouvez laisser l'ordinateur prendre la décision pour vous (c'est à cela que servent les ordinateurs). Il vaut mieux que les paramètres JVM soient toujours optimaux à 95% que de forcer une "collection toujours agressive de 8 cœurs pour des temps de pause plus bas" sur toutes les boîtes, la moitié d'entre elles étant t2.small à la fin.

Exception : lorsque l'application est livrée avec un guide de performances et un réglage spécifique en place. Il est parfaitement normal de laisser les paramètres fournis tels quels.

Astuce : Passer à une JVM plus récente pour bénéficier des dernières améliorations peut parfois fournir un bon coup de pouce sans trop d'effort.

Cas spécial: -XX: + UseCompressedOops

La JVM a un paramètre spécial qui force l'utilisation de l'index 32 bits en interne (lire: comme des pointeurs). Cela permet d'adresser 4 294 967 295 objets * 8 octets d'adresse => 32 Go de mémoire. (À ne pas confondre avec l'espace d'adressage de 4 Go pour les pointeurs RÉELS).

Il réduit la consommation globale de mémoire avec un impact positif potentiel sur tous les niveaux de mise en cache.

Exemple réel : la documentation ElasticSearch indique qu'un nœud 32 Go 32 bits en cours d'exécution peut être équivalent à un nœud 40 Go 64 bits en termes de données réelles conservées en mémoire.

Une note sur l'histoire : Le drapeau était connu pour être instable à l'ère pré-Java-7 (peut-être même pré-Java-6). Cela fonctionne parfaitement dans la nouvelle machine virtuelle Java depuis un certain temps.

Améliorations des performances de la machine virtuelle Java HotSpot ™

[...] Dans Java SE 7, l'utilisation de oops compressés est la valeur par défaut pour les processus JVM 64 bits lorsque -Xmx n'est pas spécifié et pour les valeurs de -Xmx inférieures à 32 gigaoctets. Pour JDK 6 avant la version 6u23, utilisez l'indicateur -XX: + UseCompressedOops avec la commande Java pour activer la fonction.

Voir : Encore une fois, la JVM a des années d'avance sur le réglage manuel. Pourtant, c'est intéressant de le savoir =)

Cas spécial: -XX: + UseNUMA

L'accès à la mémoire non uniforme (NUMA) est une conception de mémoire d'ordinateur utilisée dans le multitraitement, le temps d'accès à la mémoire dépend de l'emplacement de la mémoire par rapport au processeur. Source: Wikipedia

Les systèmes modernes ont des architectures de mémoire extrêmement complexes avec plusieurs couches de mémoire et de caches, privées et partagées, à travers les cœurs et le CPU.

De toute évidence, l'accès à des données dans le cache L2 dans le processeur actuel est BEAUCOUP plus rapide que d'avoir à aller jusqu'à une clé USB à partir d'un autre socket.

Je crois que tous les systèmes à prises multiples vendus aujourd'hui sont NUMA de par leur conception, alors que tous les systèmes grand public ne le sont PAS. Vérifiez si votre serveur prend en charge NUMA avec la commande numactl --show sur linux.

L'indicateur compatible NUMA indique à la JVM d'optimiser les allocations de mémoire pour la topologie matérielle sous-jacente.

L'amélioration des performances peut être substantielle (c'est-à-dire à deux chiffres: + XX%). En fait, quelqu'un qui passe d'un "NOT-NUMA 10CPU 100GB" à un "NUMA 40CPU 400GB" pourrait subir une perte [dramatique] de performances s'il ne connaît pas le drapeau.

Remarque : Il existe des discussions pour détecter NUMA et définir automatiquement l'indicateur dans la JVM http://openjdk.Java.net/jeps/ 16

Bonus : Toutes les applications destinées à fonctionner sur un gros matériel (c'est-à-dire NUMA) doivent être optimisées pour cela. Il n'est pas spécifique aux applications Java.

Vers l'avenir: -XX: + UseG1GC

La dernière amélioration de Garbage Collection est le collecteur G1 (lire: Garbage First) .

Il est destiné aux systèmes à cœur élevé et à mémoire élevée. Au minimum absolu 4 cœurs + 6 Go de mémoire. Il est destiné aux bases de données et aux applications gourmandes en mémoire utilisant 10 fois plus que cela.

Version courte, à ces tailles, le GC traditionnel est confronté à trop de données à traiter en même temps et les pauses deviennent incontrôlables. Le G1 divise le tas en plusieurs petites sections qui peuvent être gérées indépendamment et en parallèle pendant que l'application est en cours d'exécution.

La première version était disponible en 2013. Elle est suffisamment mûre pour la production, mais elle ne sera pas utilisée par défaut de sitôt. Cela vaut la peine d'essayer pour les grandes applications.

Ne touchez pas: Tailles de génération (NewGen, PermGen ...)

Le GC a divisé la mémoire en plusieurs sections. (Sans entrer dans les détails, vous pouvez google "Java GC Generations".)

La dernière fois que j'ai passé une semaine à essayer 20 combinaisons différentes de drapeaux de générations sur une application prenant 10 000 hit/s. J'obtenais un magnifique boost allant de -1% à +1%.

Les générations Java GC sont un sujet intéressant pour lire des articles ou en écrire un. Ce n'est pas une chose à régler, sauf si vous faites partie des 1% qui peuvent consacrer du temps substantiel à des gains négligeables parmi les 1% de personnes qui ont vraiment besoin d'optimisations.

Conclusion

J'espère que cela peut vous aider. Amusez-vous avec la JVM.

Java est le meilleur langage et la meilleure plateforme du monde! Allez répandre l'amour: D

30
user5994461

Regardez ici (ou effectuez une recherche Google pour le réglage du hotspot) http://Java.Sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

Vous voulez certainement profiler votre application avant d'essayer de régler la vm. NetBeans a un profileur Nice intégré qui vous permettra de voir toutes sortes de choses.

Une fois, quelqu'un m'a dit que le GC était cassé pour leur application - j'ai regardé le code et j'ai constaté qu'ils n'avaient jamais fermé aucun des résultats de leurs requêtes de base de données, donc ils conservaient des quantités massives de tableaux d'octets. Une fois que nous avons fermé les résultats, le temps est passé de plus de 20 minutes et un Go de mémoire à environ 2 minutes et une très petite quantité de mémoire. Ils ont réussi à supprimer les paramètres de réglage de la JVM et les choses étaient heureuses.

7
TofuBeer

Java sur machine Windows 32 bits, vos choix sont limités. D'après mon expérience, le réglage des paramètres suivants aura un impact sur les performances de l'application:

  1. tailles de mémoire
  2. choix des collecteurs GC
  3. paramètres liés aux collecteurs GC
1
stones333

Je vous suggère de profiler votre application avec l'échantillonnage du processeur et la surveillance de l'allocation des objets activés en même temps. Vous constaterez que vous obtenez des résultats très différents qui peuvent être utiles pour régler votre code. Essayez également d'utiliser le profileur hprof intégré, il peut également donner des résultats très différents.

En général, le profilage de votre application fait beaucoup plus de différence que les arguments JVM.

1
Peter Lawrey

La meilleure façon absolue de répondre à cette question est d'effectuer des tests contrôlés sur l'application dans un environnement de production aussi proche que possible. Il est tout à fait possible que l'utilisation de -server, une taille de segment de départ raisonnable et le comportement relativement intelligent des JVM récentes se comportent aussi bien ou mieux que la grande majorité des paramètres que l'on essaierait normalement.

Il existe une exception spécifique à cette large généralisation: dans le cas où vous exécutez dans un conteneur Web, il y a de fortes chances que vous souhaitiez augmenter les paramètres de génération permanente.

1
mmDonuts

Cela dépendra fortement de votre application, du fournisseur et de la version de la JVM. Vous devez être clair sur ce que vous considérez comme un problème de performances. Êtes-vous concerné par certaines sections critiques du code? Avez-vous déjà profilé l'application? La machine virtuelle Java passe-t-elle trop de temps à ramasser les ordures?

Je commencerais probablement par l'option -verbose: gc JVM pour observer le fonctionnement du ramasse-miettes. Plusieurs fois, le correctif le plus simple consiste simplement à augmenter la taille maximale du tas avec -Xmx. Si vous apprenez à interpréter la sortie -verbose: gc, il vous dira presque tout ce que vous devez savoir sur le réglage de la JVM dans son ensemble. Mais faire cela seul ne fera pas comme par magie accélérer un code mal réglé. La plupart des options de réglage JVM sont conçues pour améliorer les performances du garbage collector et/ou des tailles de mémoire.

Pour le profilage, j'aime yourkit.com

0
Gary