Question

Comment trouver une fuite de mémoire en Java (en utilisant, par exemple, JHat) ?J'ai essayé de charger le tas dans JHat pour avoir un aperçu de base.Cependant, je ne comprends pas comment je suis censé pouvoir trouver la référence racine (réf) ou quel que soit son nom.En gros, je peux dire qu'il y a plusieurs centaines de mégaoctets d'entrées de table de hachage ([java.util.HashMap$Entry ou quelque chose comme ça), mais les cartes sont utilisées partout...Existe-t-il un moyen de rechercher de grandes cartes, ou peut-être de trouver les racines générales d'arborescences d'objets volumineux ?

Modifier] Ok, j'ai lu les réponses jusqu'à présent, mais disons simplement que je suis un salaud bon marché (ce qui signifie que je suis plus intéressé à apprendre à utiliser Jhat que à payer pour JProfiler).De plus, JHat est toujours disponible puisqu'il fait partie du JDK.À moins bien sûr qu'il n'y ait d'autre moyen avec JHat que la force brute, mais je ne peux pas croire que cela puisse être le cas.

De plus, je ne pense pas pouvoir réellement modifier (ajout de la journalisation des tous tailles de carte) et exécutez-le suffisamment longtemps pour que je remarque la fuite.

Était-ce utile?

La solution

J'utilise l'approche suivante pour rechercher des fuites de mémoire en Java.J'ai utilisé jProfiler avec beaucoup de succès, mais je pense que tout outil spécialisé doté de capacités graphiques (les différences sont plus faciles à analyser sous forme graphique) fonctionnera.

  1. Démarrez l'application et attendez qu'elle atteigne l'état "stable", lorsque toutes les initialisations sont terminées et que l'application est inactive.
  2. Exécutez plusieurs fois l'opération suspectée de produire une fuite de mémoire pour permettre à toute initialisation liée au cache et à la base de données d'avoir lieu.
  3. Exécutez GC et prenez un instantané de la mémoire.
  4. Relancez l'opération.En fonction de la complexité de l'opération et de la taille des données traitées, l'opération peut devoir être exécutée plusieurs fois.
  5. Exécutez GC et prenez un instantané de la mémoire.
  6. Exécutez une comparaison pour 2 instantanés et analysez-la.

Fondamentalement, l'analyse doit commencer par la plus grande différence positive, par exemple, par types d'objets et trouver ce qui fait que ces objets supplémentaires restent en mémoire.

Pour les applications Web qui traitent les requêtes dans plusieurs threads, l'analyse devient plus compliquée, mais l'approche générale s'applique néanmoins.

J'ai réalisé un certain nombre de projets visant spécifiquement à réduire l'empreinte mémoire des applications et cette approche générale avec quelques ajustements et astuces spécifiques à l'application a toujours bien fonctionné.

Autres conseils

Question ici, je dois dire que le fait d'avoir un outil qui ne prend pas 5 minutes pour répondre à un clic facilite grandement la recherche de fuites de mémoire potentielles.

Étant donné que les gens suggèrent plusieurs outils (je n'ai essayé que Visual wm depuis que je l'ai obtenu dans l'essai JDK et JProbe), j'ai pensé que je devrais suggérer un outil gratuit / open source construit sur la plate-forme Eclipse, le Memory Analyzer (parfois référencé comme la mémoire SAP analyseur) disponible sur http://www.eclipse.org/mat/ .

Ce qui est vraiment cool avec cet outil, c'est qu'il a indexé le vidage du tas lorsque je l'ai ouvert pour la première fois, ce qui lui a permis d'afficher des données comme le tas conservé sans attendre 5 minutes pour chaque objet (presque toutes les opérations étaient beaucoup plus rapides que les autres outils que j'ai essayés) .

Lorsque vous ouvrez la décharge, le premier écran vous montre un diagramme circulaire avec les plus gros objets (en comptant le tas conservé) et vous pouvez rapidement naviguer jusqu'aux objets trop gros pour plus de confort.Il dispose également d'un outil de recherche de fuites probables qui, à mon avis, peut s'avérer utile, mais comme la navigation me suffisait, je ne m'y suis pas vraiment lancé.

Un outil est d’une grande aide.

Cependant, il arrive parfois que vous ne puissiez pas utiliser un outil :le vidage du tas est si énorme qu'il plante l'outil, vous essayez de dépanner une machine dans un environnement de production auquel vous n'avez qu'un accès shell, etc.

Dans ce cas, il est utile de connaître le fichier de vidage hprof.

Recherchez SITES COMMENCER.Cela vous montre quels objets utilisent le plus de mémoire.Mais les objets ne sont pas regroupés uniquement par type :chaque entrée comprend également un identifiant de « trace ».Vous pouvez ensuite rechercher ce "TRACE nnnn" pour voir les premières images de la pile où l'objet a été alloué.Souvent, une fois que je vois où l'objet est alloué, je trouve un bug et j'ai fini.Notez également que vous pouvez contrôler le nombre d'images enregistrées dans la pile avec les options de -Xrunhprof.

Si vous consultez le site d'allocation et que vous ne voyez rien d'anormal, vous devez commencer le chaînage en arrière de certains de ces objets actifs vers les objets racine, pour trouver la chaîne de référence inattendue.C'est là qu'un outil est vraiment utile, mais vous pouvez faire la même chose à la main (enfin, avec grep).Il n’y a pas qu’un seul objet racine (c’est-à-dire un objet non soumis au garbage collection).Les threads, les classes et les cadres de pile agissent comme des objets racine, et tout ce qu'ils référencent fortement n'est pas collectable.

Pour effectuer le chaînage, recherchez dans la section HEAP DUMP les entrées avec le mauvais identifiant de trace.Cela vous mènera à une entrée OBJ ou ARR, qui affiche un identifiant d'objet unique en hexadécimal.Recherchez toutes les occurrences de cet identifiant pour trouver qui a une référence forte à l'objet.Suivez chacun de ces chemins en arrière au fur et à mesure qu'ils se ramifient jusqu'à ce que vous trouviez où se trouve la fuite.Vous voyez pourquoi un outil est si pratique ?

Les membres statiques sont des récidivistes en matière de fuites de mémoire.En fait, même sans outil, cela vaudrait la peine de consacrer quelques minutes à parcourir votre code pour rechercher les membres statiques de la carte.Une carte peut-elle s'agrandir ?Est-ce que quelque chose nettoie ses entrées ?

La plupart du temps, dans les applications d'entreprise, le tas Java donné est plus grand que la taille idéale de 12 à 16 Go maximum.J'ai eu du mal à faire fonctionner le profileur NetBeans directement sur ces grosses applications Java.

Mais ce n’est généralement pas nécessaire.Vous pouvez utiliser l'utilitaire jmap fourni avec le jdk pour effectuer un vidage de tas "en direct", c'est-à-dire que jmap videra le tas après avoir exécuté GC.Effectuez une opération sur l'application, attendez que l'opération soit terminée, puis effectuez un autre vidage de tas "en direct".Utilisez des outils comme Eclipse MAT pour charger les tas, trier sur l'histogramme, voir quels objets ont augmenté ou lesquels sont les plus élevés, cela donnerait un indice.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

Il n’y a qu’un seul problème avec cette approche ;D'énormes vidages de tas, même avec l'option live, peuvent être trop volumineux pour être transférés au cycle de développement et peuvent nécessiter une machine avec suffisamment de mémoire/RAM pour s'ouvrir.

C’est là qu’intervient l’histogramme de classe.Vous pouvez créer un histogramme de classe en direct avec l'outil jmap.Cela donnera uniquement l'histogramme de classe d'utilisation de la mémoire. Fondamentalement, il n'aura pas les informations nécessaires pour enchaîner la référence.Par exemple, il peut placer un tableau de caractères en haut.Et la classe String quelque part en dessous.Vous devez établir le lien vous-même.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Au lieu de prendre deux vidages de tas, prenez deux histogrammes de classe, comme décrit ci-dessus ;Comparez ensuite les histogrammes de classes et voyez les classes qui augmentent.Voyez si vous pouvez associer les classes Java à vos classes d'application.Cela donnera un assez bon indice.Voici un script pythons qui peut vous aider à comparer deux vidages d'histogramme jmap. histogrammeparser.py

Enfin des outils comme JConolse et VisualVm sont indispensables pour voir la croissance de la mémoire au fil du temps, et voir s'il y a une fuite de mémoire.Enfin, parfois, votre problème n'est peut-être pas une fuite de mémoire, mais une utilisation élevée de la mémoire. Pour cela, activez la journalisation GC ; utilisez un GC de compactage plus avancé et nouveau comme G1GC ;et vous pouvez utiliser des outils jdk comme jstat pour voir le comportement du GC en direct

jstat -gccause pid <optional time interval>

Autres références à Google pour -jhat, jmap, Full GC, Humongous allocation, G1GC

Il existe des outils qui devraient vous aider à trouver votre fuite, comme JProbe, YourKit, AD4J ou JRockit Mission Control.Ce dernier est celui que je connais personnellement le mieux.Tout bon outil devrait vous permettre d'explorer jusqu'à un niveau où vous pouvez facilement identifier les fuites et où les objets qui fuient sont attribués.

L'utilisation de HashTables, de Hashmaps ou similaires est l'un des rares moyens par lesquels vous pouvez réellement perdre de la mémoire en Java.Si je devais trouver la fuite à la main, j'imprimerais périodiquement la taille de mes HashMaps, et à partir de là, je trouverais celui où j'ajoute des éléments et j'oublierais de les supprimer.

Eh bien, il existe toujours une solution low-tech consistant à ajouter une journalisation de la taille de vos cartes lorsque vous les modifiez, puis à rechercher les journaux pour lesquels les cartes dépassent une taille raisonnable.

NetBeans dispose d'un profileur intégré.

Vous devez vraiment utiliser un profileur de mémoire qui suit les allocations.Jeter un coup d'œil à JProfiler - leur fonctionnalité "heap walker" est excellente et ils sont intégrés à tous les principaux IDE Java.Ce n'est pas gratuit, mais ce n'est pas si cher non plus (499 $ pour une seule licence) - vous perdrez assez rapidement 500 $ en luttant pour trouver une fuite avec des outils moins sophistiqués.

vous voudrez peut-être vérifier jconsole.Il fait également partie du JDK et j'ai trouvé utile de rechercher des fuites de mémoire/référence en conjonction avec jhat.Jetez également un oeil à ce entrée de blog.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top