Après avoir basculé notre application Java (services exécutés sur Tomcat) JRE de Java 7 à Java 8, nous avons commencé à voir Java.lang.OutOfMemoryError: Metaspace
après quelques jours d'exécution avec un volume de trafic élevé.
L'utilisation du tas était correcte. Le méta-espace saute après le moment où le même flux de code a été exécuté lors des tests de performances.
Quelles pourraient être les causes possibles du problème de mémoire métaspace?
Les paramètres actuels sont:
-server -Xms8g -Xmx8g -XX:MaxMetaspaceSize=3200m -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC -XX:MaxGCPauseMillis=1000
-XX:+DisableExplicitGC -XX:+PrintGCDetails
-XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=7 -XX:NewSize=5004m
-XX:MaxNewSize=5004m -XX:MaxTenuringThreshold=12
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintFlagsFinal
-XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution
-XX:+PrintGCCause -XX:+PrintAdaptiveSizePolicy
-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=3 -XX:GCLogFileSize=200M
En outre, l'application utilise beaucoup de réflexion. Nous utilisons également un chargeur de classes personnalisé. Tous fonctionnaient bien dans Java 7.
Je suppose que vous pouvez créer le problème avec la même demande (ensemble de demandes) sur une période donnée ... C'est une bonne chose que vous ayez défini MaxMetaspaceSize, sinon l'application utilisera la mémoire native jusqu'à épuisement de sa capacité ... .. Mais je vais commencer par les étapes suivantes:
-Dcom.Sun.management.jmxremote -Dcom.Sun.management.jmxremote.port=9999 -Dcom.Sun.management.jmxremote.authenticate=false -Dcom.Sun.management.jmxremote.ssl=false -XX:+UnlockDiagnosticVMOptions
Une fois que vous avez connecté visualvm (jvisualvm) à la machine virtuelle Java, cliquez sur le moniteur, puis voyez le nombre de classes chargées. Là, vous pouvez surveiller le tas ainsi que le méta-espace. Mais je vais ajouter les autres outils pour surveiller de près le métaspace.
sélectionnez map (sort (map (heap.objects ('Java.lang.ClassLoader'), '' chargeur: it, compte: it.classes.elementCount} '),' lhs.count <rhs.count ') , 'toHtml (it) + "
"')
Mais la requête ci-dessus nommée "classe chargée par le chargeur de classe" sera lente, ce qui affichera les classes chargées par chaque chargeur de classe.
select { loader: cl,
classes: filter(map(cl.classes.elementData, 'it'), 'it != null') }
from instanceof Java.lang.ClassLoader cl
jmc
pour vous connecter à la VM, puis une fois que vous êtes connecté, cliquez sur "Commandes de diagnostic" dans la JMC, qui se trouve dans le coin supérieur droit. Puisque nous avons activé UnlockDiagnosticVMOptions, GC.class_stats peut être exécuté. Vous voudrez peut-être l'exécuter avec afficher toutes les colonnes et imprimer au format csv. Donc, la commande ressemblera à:
GC.class_stats -all=true -csv=true
Vous pouvez ensuite comparer les statistiques de classe sur différentes périodes et déterminer quelles classes sont à l'origine du problème (croissance de métaspace) ou quelles classes ont des informations associées (méthode/données de méthode) dans le métaspace. Comment analyser les sorties csv collectées au fil du temps: eh bien, je prendrais ce csv et le chargerais dans deux tables similaires (représentant csv) dans une base de données ou un autre endroit pour comparer les sorties csv de GC.class_stats où je peux exécuter du SQL ou tout autre outil d'analyse. Cela donnerait une meilleure idée de ce qui se développe exactement dans le méta-espace . Les statistiques de classe GC ont les colonnes suivantes:
Index, Super, InstSize, InstCount, InstBytes, Miroir, KlassBytes, K_secondary_supers, VTAB, CCFI, OopMap, IK_methods, IK_method_ordering, IK_default_methods, IK_default_vtable_indices, IK_local_interfaces, IK_transitive_interfaces, IK_fields, IK_inner_classes, IK_signers, class_annotations, class_type_annotations, fields_annotations, fields_type_annotations, methods_annotations, method_parameter_annotations, method_type_annotations, method_default_annotations, annotations, Cp, CpTags, CpCache, CpOperands, CpRefMap, CpAll, MethodCount, MethodBytes, ConstMethod, MethodP
J'espère que ça aide. En outre, il semble que le bogue soit lié à Java 8 s'il ne provoque aucune fuite dans la version 1.7.
De plus, les classes ne seront pas déchargées du méta-espace si l'une d'entre elles contient une référence à classloader. Si vous savez que vos chargeurs de classe sont supposés être GCed et que personne ne devrait détenir la référence à votre chargeur de classes, vous pouvez revenir au tas dump dans visualvm et cliquer sur l'instance du chargeur de classes et cliquer avec le bouton droit de la souris pour trouver la "racine GC la plus proche". vous qui détient la référence aux classloaders.
nous avions un problème similaire et la cause première était que les fichiers de classe 60K étaient chargés dans la mémoire métaspace, mais rien ne se déchargeait.L'addition en dessous de JVM arg corrigeait le problème.
-Dcom.Sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true
https://issues.Apache.org/jira/browse/CXF-2939
J'espère que cela t'aides.
également si certains déploient automatiquement, comme Tomcat, NE gardez PAS les sauvegardes dans Tomcat\webapps, sinon il essaiera de charger la sauvegarde et d'entrer en collision avec ces ressources.