Wie finden Sie einen Speicherverlust in Java (z. B. mit JHat)? Ich habe versucht, den Heap-Dump in JHat zu laden, um einen grundlegenden Überblick zu erhalten. Ich verstehe jedoch nicht, wie ich die Stammreferenz ( ref ) oder wie auch immer sie heißen soll finden soll. Grundsätzlich kann ich feststellen, dass es mehrere hundert Megabyte an Hash-Tabelleneinträgen gibt ([java.util.HashMap $ Entry oder ähnliches), aber Karten werden überall verwendet ... Gibt es eine Möglichkeit, nach großen Karten zu suchen? , oder vielleicht allgemeine Wurzeln großer Objektbäume finden?
[Bearbeiten] Ok, ich habe die Antworten bisher gelesen, aber sagen wir einfach, ich bin ein billiger Bastard (was bedeutet, dass ich mehr daran interessiert bin, den Umgang mit JHat zu lernen, als für JProfiler zu bezahlen). Außerdem ist JHat immer verfügbar, da es Teil des JDK ist. Es sei denn natürlich, es gibt keinen Weg mit JHat außer roher Gewalt, aber ich kann nicht glauben, dass dies der Fall sein kann.
Ich glaube auch nicht, dass ich tatsächlich Änderungen vornehmen (Protokollierung aller Kartengrößen hinzufügen ) und es so lange ausführen kann, bis ich das Leck bemerke.
quelle
Antworten:
Ich verwende den folgenden Ansatz, um Speicherlecks in Java zu finden. Ich habe jProfiler mit großem Erfolg verwendet, aber ich glaube, dass jedes spezielle Tool mit Grafikfunktionen (Unterschiede sind in grafischer Form einfacher zu analysieren) funktioniert.
Grundsätzlich sollte die Analyse vom größten positiven Unterschied beispielsweise nach Objekttypen ausgehen und herausfinden, warum diese zusätzlichen Objekte im Speicher bleiben.
Für Webanwendungen, die Anforderungen in mehreren Threads verarbeiten, wird die Analyse komplizierter, es gilt jedoch weiterhin ein allgemeiner Ansatz.
Ich habe eine ganze Reihe von Projekten durchgeführt, die speziell darauf abzielen, den Speicherbedarf der Anwendungen zu verringern, und dieser allgemeine Ansatz mit einigen anwendungsspezifischen Optimierungen und Tricks hat immer gut funktioniert.
quelle
Fragender hier, ich muss sagen, dass das Erhalten eines Tools, das keine 5 Minuten benötigt, um einen Klick zu beantworten, das Auffinden potenzieller Speicherlecks erheblich erleichtert.
Da die Leute mehrere Tools vorschlagen (ich habe Visual Wm nur ausprobiert, seit ich das in der JDK- und JProbe-Testversion erhalten habe), sollte ich ein Free / Open Source-Tool vorschlagen, das auf der Eclipse-Plattform basiert, den Memory Analyzer (manchmal auch als SAP-Speicher bezeichnet) Analysator) verfügbar unter http://www.eclipse.org/mat/ .
Was an diesem Tool wirklich cool ist, ist, dass es den Heap-Dump beim ersten Öffnen indiziert hat, sodass Daten wie beibehaltener Heap angezeigt werden konnten, ohne 5 Minuten auf jedes Objekt zu warten (so gut wie alle Vorgänge waren tonnenweise schneller als die anderen Tools, die ich ausprobiert habe). .
Wenn Sie den Speicherauszug öffnen, wird auf dem ersten Bildschirm ein Kreisdiagramm mit den größten Objekten angezeigt (wobei der verbleibende Heap gezählt wird), und Sie können schnell zu den Objekten navigieren, die für den Komfort zu groß sind. Es hat auch einen Find wahrscheinlichen Leckverdächtigen, den ich als nützlich erachten kann, aber da die Navigation für mich ausreichte, bin ich nicht wirklich darauf gekommen.
quelle
HeapDumpOnCtrlBreak
Parameter in Java 5 und höher nicht verfügbar . Die Lösung, die ich gefunden habe (bisher noch gesucht), besteht darin, JMap zum Speichern der.hprof
Datei zu verwenden, die ich dann in Eclipse eingefügt und mit MAT untersucht habe.Ein Werkzeug ist eine große Hilfe.
Es gibt jedoch Situationen, in denen Sie kein Tool verwenden können: Der Heap-Dump ist so groß, dass das Tool abstürzt. Sie versuchen, eine Maschine in einer Produktionsumgebung zu beheben, auf die Sie nur Shell-Zugriff haben usw.
In diesem Fall ist es hilfreich, sich in der hprof-Dump-Datei zurechtzufinden.
Suchen Sie nach SITES BEGIN. Dies zeigt Ihnen, welche Objekte den meisten Speicher belegen. Die Objekte werden jedoch nicht nur nach Typ zusammengefasst: Jeder Eintrag enthält auch eine "Trace" -ID. Sie können dann nach "TRACE nnnn" suchen, um die obersten Frames des Stapels anzuzeigen, in dem das Objekt zugewiesen wurde. Sobald ich sehe, wo das Objekt zugeordnet ist, finde ich oft einen Fehler und bin fertig. Beachten Sie außerdem, dass Sie mit den Optionen -Xrunhprof steuern können, wie viele Frames im Stapel aufgezeichnet werden.
Wenn Sie die Zuordnungssite auschecken und keine Fehler feststellen, müssen Sie von einigen dieser Live-Objekte mit Stammobjekten rückwärts verketten, um die unerwartete Referenzkette zu finden. Hier hilft ein Tool wirklich, aber Sie können das Gleiche von Hand tun (na ja, mit grep). Es gibt nicht nur ein Stammobjekt (dh ein Objekt, das keiner Speicherbereinigung unterliegt). Threads, Klassen und Stapelrahmen fungieren als Stammobjekte, und alles, worauf sie stark verweisen, kann nicht gesammelt werden.
Um die Verkettung durchzuführen, suchen Sie im Abschnitt HEAP DUMP nach Einträgen mit der falschen Trace-ID. Dadurch gelangen Sie zu einem OBJ- oder ARR-Eintrag, in dem eine eindeutige Objektkennung hexadezimal angezeigt wird. Suchen Sie nach allen Vorkommen dieser ID, um herauszufinden, wer einen starken Bezug zum Objekt hat. Folgen Sie jedem dieser Pfade rückwärts, während Sie sich verzweigen, bis Sie herausfinden, wo das Leck ist. Sehen Sie, warum ein Werkzeug so praktisch ist?
Statische Mitglieder sind ein Wiederholungstäter für Speicherlecks. Selbst ohne ein Tool lohnt es sich, ein paar Minuten in Ihrem Code nach statischen Map-Mitgliedern zu suchen. Kann eine Karte groß werden? Bereinigt irgendetwas jemals seine Einträge?
quelle
jhat
undMAT
offenbar versuchen , die ganze Heapdump in dem Speicher zu laden, und so typischerweise mit einem AbsturzOutOfMemoryError
auf große Deponien (dh von den Anwendungen , die die meisten Heap - Analyse benötigt! ). Der NetBeans Profiler scheint einen anderen Algorithmus zum Indizieren von Referenzen zu verwenden, der bei großen Speicherauszügen langsam werden kann, aber zumindest keinen unbegrenzten Speicher im Tool verbraucht und abstürzt.In Unternehmensanwendungen ist der angegebene Java-Heap meistens größer als die ideale Größe von maximal 12 bis 16 GB. Ich habe es schwierig gefunden, den NetBeans-Profiler direkt auf diesen großen Java-Apps zum Laufen zu bringen.
Aber normalerweise wird dies nicht benötigt. Sie können das mit dem jdk gelieferte Dienstprogramm jmap verwenden, um einen "Live" -Heap-Dump zu erstellen. Das heißt, jmap gibt den Heap nach dem Ausführen von GC aus. Führen Sie eine Operation für die Anwendung aus, warten Sie, bis die Operation abgeschlossen ist, und führen Sie dann einen weiteren "Live" -Heap-Dump durch. Verwenden Sie Tools wie Eclipse MAT, um die Heapdumps zu laden, das Histogramm zu sortieren und festzustellen, welche Objekte zugenommen haben oder welche am höchsten sind. Dies würde einen Hinweis geben.
Bei diesem Ansatz gibt es nur ein Problem. Riesige Heap-Dumps sind selbst mit der Live-Option möglicherweise zu groß, um in die Entwicklungsrunde übertragen zu werden, und benötigen möglicherweise einen Computer mit genügend Speicher / RAM zum Öffnen.
Hier kommt das Klassenhistogramm ins Spiel. Sie können ein Live-Klassenhistogramm mit dem jmap-Tool sichern. Dies gibt nur das Klassenhistogramm der Speichernutzung an. Grundsätzlich verfügt es nicht über die Informationen, um die Referenz zu verketten. Zum Beispiel kann es ein char-Array an die Spitze setzen. Und String-Klasse irgendwo unten. Sie müssen die Verbindung selbst herstellen.
Anstatt zwei Heap-Dumps zu erstellen, nehmen Sie zwei Klassenhistogramme wie oben beschrieben. Vergleichen Sie dann die Klassenhistogramme und sehen Sie die Klassen, die zunehmen. Überprüfen Sie, ob Sie die Java-Klassen mit Ihren Anwendungsklassen verknüpfen können. Dies wird einen ziemlich guten Hinweis geben. Hier ist ein Python-Skript, mit dem Sie zwei jmap-Histogramm-Dumps vergleichen können. histogramparser.py
Schließlich sind Tools wie JConolse und VisualVm unerlässlich, um das Speicherwachstum im Laufe der Zeit zu beobachten und festzustellen, ob ein Speicherverlust vorliegt. Schließlich ist Ihr Problem manchmal nicht ein Speicherverlust, sondern eine hohe Speichernutzung. Verwenden Sie dazu die GC-Protokollierung. Verwenden Sie einen fortschrittlicheren und neueren Komprimierungs-GC wie G1GC. und Sie können JDK-Tools wie jstat verwenden, um das GC-Verhalten live zu sehen
Andere Referenzen zu googeln für -jhat, jmap, Full GC, Humongous Allocation, G1GC
quelle
Es gibt Tools wie JProbe, YourKit, AD4J oder JRockit Mission Control, die Ihnen beim Auffinden Ihres Lecks helfen sollen. Der letzte ist der, den ich persönlich am besten kenne. Mit jedem guten Werkzeug sollten Sie einen Drilldown bis zu einer Ebene durchführen können, auf der Sie leicht erkennen können, welche Lecks vorhanden sind und wo die undichten Objekte zugeordnet sind.
Die Verwendung von HashTables, Hashmaps oder ähnlichem ist eine der wenigen Möglichkeiten, mit denen Sie tatsächlich Speicher in Java verlieren können. Wenn ich das Leck von Hand finden müsste, würde ich die Größe meiner HashMaps peridisch drucken und von dort diejenige finden, bei der ich Elemente hinzufüge und vergesse, sie zu löschen.
quelle
Nun, es gibt immer die Low-Tech-Lösung, die Protokollierung der Größe Ihrer Karten hinzuzufügen, wenn Sie sie ändern, und dann die Protokolle zu durchsuchen, nach denen Karten über eine vernünftige Größe hinauswachsen.
quelle
NetBeans verfügt über einen integrierten Profiler.
quelle
Sie müssen wirklich einen Speicherprofiler verwenden, der die Zuordnungen verfolgt. Schauen Sie sich JProfiler an - die Funktion "Heap Walker" ist großartig und sie sind in alle wichtigen Java-IDEs integriert. Es ist nicht kostenlos, aber auch nicht so teuer (499 US-Dollar für eine Einzellizenz) - Sie werden ziemlich schnell Zeit im Wert von 500 US-Dollar verbrauchen, wenn Sie mit weniger ausgefeilten Tools Schwierigkeiten haben, ein Leck zu finden.
quelle
Sie können dies herausfinden, indem Sie die Größe der Speichernutzung messen, nachdem Sie Garbage Collector mehrmals aufgerufen haben:
Wenn die Ausgabenummern gleich waren, liegt in Ihrer Anwendung kein Speicherverlust vor. Wenn Sie jedoch einen Unterschied zwischen der Anzahl der Speichernutzungen festgestellt haben (zunehmende Anzahl), liegt in Ihrem Projekt ein Speicherverlust vor. Beispielsweise:
Beachten Sie, dass es manchmal einige Zeit dauert, Speicher durch bestimmte Aktionen wie Streams und Sockets freizugeben. Sie sollten nicht nach den ersten Ausgaben urteilen, sondern sie in einer bestimmten Zeit testen.
quelle
Testen Sie diesen Bildschirm, um Speicherlecks mit JProfiler zu finden. Es ist eine visuelle Erklärung von @Dima Malenko Answer.
Hinweis: Obwohl JProfiler keine Freeware ist, kann die Testversion die aktuelle Situation bewältigen.
quelle
Da die meisten von uns Eclipse bereits zum Schreiben von Code verwenden, sollten Sie das Memory Analyzer Tool (MAT) in Eclipse verwenden. Es funktioniert großartig.
Die Eclipse MAT ist eine Reihe von Plug-Ins für die Eclipse-IDE, die Tools zum Analysieren
heap dumps
aus einer Java-Anwendung und zum Identifizierenmemory problems
in der Anwendung bereitstellen.Dies hilft dem Entwickler, Speicherlecks mit den folgenden Funktionen zu finden
quelle