web-dev-qa-db-fra.com

Comment réduire Java échec du mode concurrentiel et gc excessif

En Java, l'échec du mode simultané signifie que le collecteur simultané n'a pas réussi à libérer suffisamment d'espace mémoire pour la génération permanente et permanente et doit abandonner et laisser le plein stop-the-world gc entre en jeu. Le le résultat final pourrait être très coûteux.

Je comprends ce concept mais je n'ai jamais eu une bonne compréhension globale de
A) ce qui pourrait provoquer une défaillance du mode simultané et
B) quelle est la solution?.

Ce type de flou me conduit à écrire/déboguer du code sans beaucoup d'indices à l'esprit et doit souvent faire le tour de ces indicateurs de performance de Foo à Bar sans raisons particulières, il suffit d'essayer.

J'aimerais apprendre des développeurs ici comment est votre expérience? Si vous aviez rencontré un tel problème de performances, quelle en était la cause et comment vous y êtes parvenu?

Si vous avez des recommandations de codage, veuillez ne pas être trop général. Merci!

44
jimx

La première chose que j'ai apprise sur le CMS est qu'il a besoin de plus de mémoire que les autres collecteurs, environ 25 à 50% de plus est un bon point de départ. Cela vous aide à éviter la fragmentation, car CMS ne fait aucun compactage comme le feraient les collectionneurs du monde. Deuxièmement, faites des choses qui aident le ramasse-miettes; Integer.valueOf au lieu du nouvel Integer, débarrassez-vous des classes anonymes, assurez-vous que les classes internes n'accèdent pas à des choses inaccessibles (privées dans la classe externe) comme ça. Moins il y a de déchets, mieux c'est. FindBugs et ne pas ignorer les avertissements vous y aideront beaucoup.

En ce qui concerne le réglage, j'ai constaté que vous devez essayer plusieurs choses:

-XX: + UseConcMarkSweepGC

Indique à JVM d'utiliser CMS dans la génération tenured.

Fixez la taille de votre tas: -Xmx2048m -Xms2048m Cela évite au GC d'avoir à faire des choses comme agrandir et réduire le tas.

-XX: + UseParNewGC

utiliser la collecte parallèle au lieu de la collecte en série dans la jeune génération. Cela accélérera vos collections mineures, surtout si vous avez une très jeune génération configurée. Une grande jeune génération est généralement bonne, mais ne dépasse pas la moitié de la taille de l'ancienne génération.

-XX: ParallelCMSThreads = X

définissez le nombre de threads que CMS utilisera lorsqu'il fera des choses qui peuvent être faites en parallèle.

-XX: + La remarque CMSParallelRemarkEnabled est en série par défaut, cela peut vous accélérer.

-XX: + CMSIncrementalMode permet à l'application de s'exécuter davantage en collant GC entre les phases

-XX: + CMSIncrementalPacing permet à la machine virtuelle Java de comprendre le changement de fréquence de collecte au fil du temps

-XX: CMSIncrementalDutyCycleMin = X Minimm durée de GC

-XX: CMSIncrementalDutyCycle = X Commencez par faire le GC ce% du temps

-XX: CMSIncrementalSafetyFactor = X

J'ai constaté que vous pouvez obtenir des temps de pause généralement bas si vous le configurez de manière à ce qu'il soit toujours en collecte. Comme la plupart du travail se fait en parallèle, vous vous retrouvez avec des pauses prévisibles essentiellement régulières.

-XX: CMSFullGCsBeforeCompaction = 1

Celui-ci est très important. Il indique au collecteur CMS de toujours terminer la collecte avant d'en commencer une nouvelle. Sans cela, vous pouvez vous retrouver dans la situation où il jette un tas de travail et recommence.

-XX: + CMSClassUnloadingEnabled

Par défaut, CMS laissera votre PermGen grandir jusqu'à ce qu'il tue votre application dans quelques semaines. Cela arrête cela. Votre PermGen ne se développerait que si vous utilisez Reflection, si vous utilisez abusivement String.intern, ou si vous faites quelque chose de mal avec un chargeur de classe, ou quelques autres choses.

Le ratio de survivant et la durée de possession peuvent également être joués, selon que vous avez des objets à longue ou à courte durée de vie et la quantité d'objets copiant entre les espaces de survivant avec lesquels vous pouvez vivre. Si vous savez que tous vos objets vont rester, vous pouvez configurer des espaces de survivant de taille zéro, et tout ce qui survit à une collection jeune génération sera immédiatement conservé.

22
Kevin Lafayette

Cité de "Comprendre les journaux de collecte de déchets de balayage de marques simultanées"

L'échec du mode simultané peut être évité en augmentant la taille de la génération permanente ou en lançant la collection CMS à un taux d'occupation moindre en définissant CMSInitiatingOccupancyFraction sur une valeur inférieure

Cependant, s'il y a vraiment une fuite de mémoire dans votre application, vous ne faites que gagner du temps.

Si vous avez besoin d'un redémarrage et d'une récupération rapides et préférez une approche 'die fast', je vous suggère de ne pas utiliser du tout CMS. Je m'en tiendrai à "-XX: + UseParallelGC".

De "Ergonomie du ramasse-miettes"

Le garbage collector parallèle (UseParallelGC) lève une exception de mémoire insuffisante si un temps excessif est passé à collecter une petite quantité du tas. Pour éviter cette exception, vous pouvez augmenter la taille du tas. Vous pouvez également définir les paramètres -XX:GCTimeLimit=time-limit et -XX:GCHeapFreeLimit=space-limit

12
fglez

Parfois, le MOO est assez rapide et se fait tuer, souffre parfois d'une longue période de gc (la dernière fois était supérieure à 10 heures).

Il me semble qu'une fuite de mémoire est à l'origine de vos problèmes.

Une défaillance du CMS ne provoquera pas (si je comprends bien) un MOO. Un échec du CMS se produit plutôt parce que la JVM doit faire trop de collections trop rapidement et que le CMS n'a pas pu suivre. Une situation où de nombreux cycles de collecte se produisent sur une courte période est lorsque votre tas est presque plein.

Le temps GC très long semble étrange ... mais est théoriquement possible si votre machine se débattait horriblement. Cependant, une longue période de GC répétés est tout à fait plausible si votre tas est presque plein.

Vous pouvez configurer le GC pour qu'il abandonne lorsque le tas est 1) à la taille maximale et 2) toujours proche de la saturation après la fin du GC. Essayez de le faire si vous ne l'avez pas déjà fait. Cela ne résoudra pas vos problèmes, mais au moins votre machine virtuelle Java obtiendra le MOO rapidement, permettant un redémarrage et une récupération de service plus rapides.

[~ # ~] modifier [~ # ~] - l'option pour ce faire est -XX:GCHeapFreeLimit=nnnnnn est un nombre compris entre 0 et 100 donnant le pourcentage minimum du tas qui doit être libre après le GC. La valeur par défaut est 2. L'option est répertoriée dans la page "La liste la plus complète des options -XX pour Java 6 JVM") page. (Il y a beaucoup de -XX options répertoriées qui n'apparaissent pas dans la documentation de Sun. Malheureusement, la page fournit peu de détails sur ce que font réellement les options.)

Vous devriez probablement commencer à chercher si votre application/application Web présente des fuites de mémoire. Si c'est le cas, vos problèmes ne disparaîtront que si ces fuites sont trouvées et corrigées. À long terme, jouer avec les options du GC Hotspot ne résoudra pas les fuites de mémoire.

4
Stephen C

J'ai trouvé en utilisant -XX:PretenureSizeThreshold=1m faire en sorte que le 'grand' objet aille immédiatement dans l'espace réservé a considérablement réduit mon jeune GC et les échecs du mode simultané car il a tendance à ne pas essayer de vider la quantité de données du jeune + 1 survivant (xmn=1536m survivorratio=3 maxTenuringThreashould=5) avant la fin d'un cycle CMS complet. Oui, mon espace de survivant est grand, mais environ une fois tous les 2 jours, quelque chose arrive dans l'application qui en aura besoin (et nous exécutons 12 serveurs d'applications chaque jour pour 1 application).

0
dave