web-dev-qa-db-fra.com

Collecteur d'ordures G1: Perm Gen remplit indéfiniment jusqu'à ce qu'un gc complet soit effectué

Nous avons une application assez grand cours d'exécution sur un serveur d'applications JBoss 7. Dans le passé, nous utilisions ParallelGC mais il nous donnait du mal dans certains serveurs où le tas était grande (5 Go ou plus) et généralement presque rempli, nous aurions très long GC fait une pause fréquemment.

Récemment, nous avons apporté des améliorations à notre utilisation de la mémoire de l'application et dans quelques cas ajouté plus RAM à certains des serveurs sur lesquels l'application est exécutée, mais nous commutation a commencé aussi à G1 dans l'espoir de faire ces pauses les choses semblent s'être améliorées moins fréquentes et/ou plus court, mais nous voyons un comportement étrange qui ne se produit pas avant (avec ParallelGC). Perm Gen semble remplir assez rapidement et une fois qu'il atteint la valeur maximale d'une pleine GC est déclenché, ce qui provoque généralement une longue pause dans les fils d'application (dans certains cas, plus de 1 minute).

Nous utilisons 512 Mo de taille perm max pendant quelques mois et au cours de notre analyse de la taille de perm cesserions généralement de plus en plus à environ 390 Mo avec ParallelGC. Après nous sommes passés à G1, cependant, le comportement ci-dessus commencé à se produire. J'ai essayé d'augmenter la taille de perm max 1 Go et même 1,5 Go, mais les GCS complètes se passe des (ils sont un peu moins fréquentes).

Dans ce lien vous pouvez voir quelques captures d'écran de l'outil de profilage que nous utilisons (YourKit Java Profiler). Remarquez que lorsque la pleine GC est déclenché l'Eden et le Vieux Gen ont beaucoup d'espace libre, mais la taille de Perm est au maximum. la taille Perm et le nombre de classes chargées diminue considérablement après la pleine GC, mais ils commencent à augmenter à nouveau et le cycle est le cache de code répété. est très bien, ne dépasse jamais 38 MB (il est de 35 Mo dans ce cas).

Voici un segment du journal de GC:

2013-11-28T11: 15: 57,774 à 0300: 64445,415: [Full GC 2126M-> 670 M (5120M), 23.6325510 secs] [Eden: 4096.0K (234.0M) -> 0.0b (256.0M) survivants: 22.0M- > 0.0b Heap: 2126.1M (5120.0M) -> 670.6M (5120.0M)] [temps: utilisateur = 10,16 sys = 0.59, real = 23.64 secs]

Vous pouvez voir le journal complet ici (à partir du moment que nous avons commencé le serveur, jusqu'à quelques minutes après le GC plein).

Voici quelques informations sur l'environnement:

Version Java "1.7.0_45"

Java (TM) SE Runtime Environment (build 1.7.0_45-b18)

Java HotSpot (TM) 64 bits serveur VM (build-B08 24,45, mode mixte)

Options de démarrage: -Xms5g -Xmx5g -Xss256k -XX:PermSize=1500M -XX:MaxPermSize=1500M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy -Xloggc:gc.log

Donc, voici mes questions:

  • Est-ce le comportement attendu avec G1? J'ai trouvé un autre poste quelque chose sur le web de quelqu'un questionnement très similaire et en disant que G1 doit effectuer des collections supplémentaires sur Perm Gen, mais il n'y avait pas de réponse ...

  • Y at-il quelque chose que je peux améliorer/corrrect dans nos paramètres de démarrage? Le serveur dispose de 8 Go de RAM, mais il ne semble pas que nous manquons de matériel, la performance de l'application est très bien jusqu'à ce qu'un GC complet est déclenché, c'est lorsque les utilisateurs connaissent de grands retards et commencent à se plaindre.

30
Jose Otavio

Causes de la croissance de Perm Gen

  • Beaucoup de classes, en particulier de JSP.
  • Beaucoup de variables statiques.
  • Il y a une fuite de classier.

Pour ceux qui ne savent pas, voici un moyen simple de penser à la façon dont la première se remplit. Le jeune Gen n'atteigne pas assez de temps pour laisser les choses expirer et ils sont donc déplacés vers l'espace de vieillesse. Le Gen Perm détient les classes des objets dans les Young and Ancien Gen. Lorsque les objets du jeune ou de l'ancien général sont collectés et que la classe n'est plus référencée, il est "non chargé" du général Perm et Le vieux général ne reçoit pas GC'D, alors non plus le général Perm et une fois qu'elle se remplit, il a besoin d'un stop complet-le-monde GC. Pour plus d'informations, voir Présentation de la génération permanente .


Basculement sur CMS

Je sais que vous utilisez G1, mais si vous passez à côté du collecteur de balayage de balayage de marque de marque concurrente (CMS) -XX:+UseConcMarkSweepGC, Essayez d'activer le déchargement de la classe et des collections de génération permanente en ajoutant -XX:+CMSClassUnloadingEnabled.


The Hidden Gotcha '

Si vous utilisez JBoss, RMI/DGC a le jeu GCInterval à 1 min. Le sous-système RMI oblige une collection complète une fois par minute. Ceci à son tour forces de la promotion au lieu de le laisser être collecté dans la jeune génération.

Vous devriez changer cela en au moins 1 heure, sinon 24 heures, afin que le GC effectue des collections appropriées.

-Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000

Liste de chaque option JVM

Pour voir toutes les options, exécutez ceci à partir de la ligne CMD.

Java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version

Si vous souhaitez voir ce que JBoss utilise, vous devez ajouter ce qui suit à votre standalone.xml. Vous obtiendrez une liste de chaque option JVM et ce qu'elle est réglée. Remarque: il doit être dans la JVM que vous souhaitez regarder pour l'utiliser. Si vous l'exécutez externe, vous ne verrez pas ce qui se passe dans la JVM que JBoss fonctionne.

set "Java_OPTS= -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal %Java_OPTS%"

Il y a un raccourci à utiliser lorsque nous ne sommes intéressés que par les drapeaux modifiés.

-XX:+PrintcommandLineFlags

Diagnostics

Utilisez JMAP pour déterminer les classes consommant des espaces de génération permanente. La sortie montrera

  • chargeur de classe
  • Nombre de cours
  • octets
  • chargeur parent
  • vivant/mort
  • taper
  • totaux

    jmap -permstat JBOSS_PID  >& permstat.out
    

Options JVM

Ces paramètres ont fonctionné pour moi, mais en fonction de la configuration de votre système et de la réalisation de votre application déterminera si elles vous conviennent.

  • -XX:SurvivorRatio=8 - Définit le ratio de survivant de 1: 8, ce qui entraîne des espaces de survivants plus importants (plus le rapport est petit, plus l'espace). La survivorratio est la taille de l'espace Eden par rapport à un espace de survivant. Les grands espaces de survivants permettent aux objets de courte durée une période plus longue pour mourir dans la jeune génération.

  • -XX:TargetSurvivorRatio=90 - Permet à 90% des espaces de survivants à occuper au lieu des 50% par défaut, permettant une meilleure utilisation de la mémoire spatiale survivants.

  • -XX:MaxTenuringThreshold=31 - Pour prévenir la promotion prématurée des jeunes à la vieille génération. Permet à des objets de courte durée une période plus longue pour mourir dans la jeune génération (et donc éviter la promotion). Une conséquence de ce paramètre est que les temps de GC mineurs peuvent augmenter en raison d'objets supplémentaires à copier. Cette valeur de la valeur et de la survivante peut avoir besoin d'être ajustées de manière à équilibrer les frais généraux de copie entre les espaces de survivants par rapport aux objets de tenure qui vont vivre pendant une longue période. Les paramètres par défaut pour CMS sont survivorratio = 1024 et MaxTenuringThreshold = 0 qui provoquent que tous les survivants d'une récupération d'une récupération doivent être encouragés. Cela peut placer beaucoup de pression sur le thread simultané unique en collectant la génération tenuine. Remarque: lorsqu'il est utilisé avec -xx: + Utilisez-lesbiasedLocking, ce paramètre doit être de 15.

  • -XX:NewSize=768m - Autoriser la spécification des premières tailles de la jeune génération

  • -XX:MaxNewSize=768m - Autoriser la spécification des tailles maximales de jeunes générations

Voici une liste plus étendue options JVM .

31
Joshua Wilson

Est-ce le comportement attendu avec G1?

Je ne trouve pas ça surprenant. L'hypothèse de base est que des choses mises dans Permgen presque jamais devient des ordures. Donc, vous vous attendez à ce que Permgen GC soit un "dernier recours"; C'est-à-dire. Quelque chose que la JVM ne ferait que si c'est forcé dans un GC complet. (OK, cet argument n'est nulle part près d'une preuve ... mais c'est cohérent avec ce qui suit.)

J'ai vu beaucoup de preuves que d'autres collectionneurs ont le même comportement; par exemple.

J'ai trouvé un autre poste sur le Web de quelqu'un qui interrogeait quelque chose de très similaire et disant que G1 devrait effectuer des collections incrémentielles sur le Gen Perm, mais il n'y avait pas de réponse ...

Je pense que j'ai trouvé le même post. Mais l'opinion de quelqu'un que c'est devrait être possible n'est pas vraiment instructif.

Y a-t-il quelque chose que je peux améliorer/corriger dans nos paramètres de démarrage?

J'en doute. Je crois comprendre que cela est inhérent à la stratégie Permgen GC.

Je vous suggère de suivre et de réparer ce qui utilise tellement Permgen en premier lieu ... ou basculez sur Java 8 dans lequel il n'y a plus de tas de PermanGen: voir - Élimination Permgen dans JDK 8

Alors qu'une fuite permanente est une explication possible, il y en a d'autres; par exemple.

  • surutilisation de String.intern(),
  • code d'application qui fait beaucoup de génération de classe dynamique; par exemple. en utilisant DynamicProxy,
  • une énorme basebase ... cependant que cela ne causerait pas Permgen Rabattez-vous Comme vous semblez être observé.
2
Stephen C

J'essaierais d'abord de trouver la cause première de la permanence de devenir plus grande avant d'essayer au hasard des options JVM.

  • Vous pouvez activer la journalisation de la classe de classement (-verbose: classe, -xx: + traceclassoading -xx: + traceclassunloading, ...) et chek out la sortie
  • Dans votre environnement de test, vous pouvez essayer de surveiller (sur JMX) lorsque des cours sont chargés (Java.lang: Type = ClassAllasscount LoadedClassCount). Cela pourrait vous aider à déterminer quelle partie de votre demande est responsable.
  • Vous pouvez également essayer de répertorier toutes les classes à l'aide des outils JVM (désolé mais j'utilise surtout principalement JRockit et vous le feriez avec JRCMD. Hope Oracle a migré ces caractéristiques utiles à Hotspot ...)

En résumé, découvrez ce qui génère tant de classes, puis réfléchissez à la réduction de la GC.

Acclamations, dimo

1
mnp

Je suis d'accord avec la réponse ci-dessus En ce que vous devriez vraiment essayer de trouver ce qui remplit réellement votre Permgen, et je soupçonne fortement que certains fuiques de Classloader vous souhaitent trouver une cause fondamentale.

Il y a - ce fil dans les forums JBoss qui traverse un couple de tels cas diagnostiqués et comment ils ont été corrigés. cette réponse et cet article Discut également de la question en général. Dans cet article, il existe une mention d'éventuellement le test le plus simple que vous puissiez faire:

Symptôme

Cela ne se produira que si vous redéployez votre application sans redémarrer le serveur d'applications. La série JBoss 4.0.x a souffert d'une fuite de classement aussi classée. En conséquence, je ne pouvais pas redéployer notre application plus de deux fois avant que la JVM ne manquerait de la mémoire et de la crash de Permgen.

Solution

Pour identifier une telle fuite, ONU-déployer votre application puis déclencher un dépotoir de tas complet (assurez-vous de déclencher un GC avant cela). Ensuite, vérifiez si vous pouvez trouver l'un de vos objets d'application dans le décharge. Si oui, suivez leurs références à leur racine et vous trouverez la cause de votre fuite de classe de classe. Dans le cas de JBoss 4.0, la seule solution était de redémarrer chaque redéployé.

C'est ce que j'essaierais d'abord, si vous pensez que le redéploiement pourrait être associé. Ce blog post est une première, faisant la même chose, mais discute également des détails. Sur la base de la publication, il serait peut-être même que vous ne redéfinissez rien, mais Permgen se remplit simplement. Dans ce cas, l'examen des classes + autre chose ajoutée à PermanGen pourrait être la voie (comme cela a déjà été mentionné dans la réponse précédente).

Si cela ne donne pas plus de perspicacité, ma prochaine étape serait en train d'essayer TOOL PLUMBR . Ils ont une sorte de garantie de la recherche de la fuite pour vous aussi.

1
eis