Frage

Wie findet man einen Speicherverlust in Java (z. B. mit JHat)?Ich habe versucht, den Heap-Dump in JHat zu laden, um einen grundlegenden Überblick zu erhalten.Allerdings verstehe ich nicht, wie ich die Root-Referenz finden soll (ref) oder wie auch immer es heißt.Grundsätzlich kann ich sagen, dass es mehrere hundert Megabyte an Hash-Tabelleneinträgen gibt ([java.util.HashMap$Entry oder so ähnlich), aber Karten werden überall verwendet ...Gibt es eine Möglichkeit, nach großen Karten zu suchen oder vielleicht allgemeine Wurzeln großer Objektbäume zu 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, Jhat zu verwenden, als für JProfiler zu bezahlen).Außerdem ist JHat immer verfügbar, da es Teil des JDK ist.Es sei denn natürlich, bei JHat gibt es keine andere Möglichkeit als rohe Gewalt, aber ich kann nicht glauben, dass das der Fall sein kann.

Außerdem glaube ich nicht, dass ich tatsächlich Änderungen vornehmen kann (Hinzufügen der Protokollierung von alle Kartengrößen) und lassen Sie es lange genug laufen, damit ich das Leck bemerken kann.

War es hilfreich?

Lösung

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 lassen sich in grafischer Form einfacher analysieren) funktionieren wird.

  1. Starten Sie die Anwendung und warten Sie, bis sie den „stabilen“ Zustand erreicht, wenn die gesamte Initialisierung abgeschlossen ist und die Anwendung inaktiv ist.
  2. Führen Sie den Vorgang, bei dem der Verdacht besteht, dass er einen Speicherverlust verursacht, mehrmals aus, damit alle Cache- und DB-bezogenen Initialisierungen stattfinden können.
  3. Führen Sie GC aus und erstellen Sie einen Speicher-Snapshot.
  4. Führen Sie den Vorgang erneut aus.Abhängig von der Komplexität des Vorgangs und der Größe der verarbeiteten Daten muss der Vorgang möglicherweise mehrmals oder mehrmals ausgeführt werden.
  5. Führen Sie GC aus und erstellen Sie einen Speicher-Snapshot.
  6. Führen Sie einen Vergleich für zwei Schnappschüsse durch und analysieren Sie ihn.

Grundsätzlich sollte die Analyse mit dem größten positiven Unterschied beispielsweise nach Objekttypen beginnen und herausfinden, was dazu führt, dass diese zusätzlichen Objekte im Speicher hängen bleiben.

Für Webanwendungen, die Anfragen in mehreren Threads verarbeiten, wird die Analyse komplizierter, aber dennoch gilt weiterhin der allgemeine Ansatz.

Ich habe eine ganze Reihe von Projekten durchgeführt, die speziell darauf abzielten, den Speicherbedarf der Anwendungen zu reduzieren, und dieser allgemeine Ansatz mit einigen anwendungsspezifischen Optimierungen und Tricks hat immer gut funktioniert.

Andere Tipps

Fragesteller hier, ich muss sagen, dass es viel einfacher ist, potenzielle Speicherlecks zu finden, wenn man sich ein Tool anschafft, das nicht 5 Minuten braucht, um auf jeden Klick zu antworten.

Da die Leute mehrere Tools vorschlagen (ich habe Visual WM nur ausprobiert, da ich das in der JDK- und JProbe-Testversion erhalten habe), sollte ich ein kostenloses/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 indizierte, was es ihm ermöglichte, Daten wie den beibehaltenen Heap anzuzeigen, ohne 5 Minuten auf jedes Objekt warten zu müssen (so ziemlich alle Vorgänge waren um einiges schneller als die anderen Tools, die ich ausprobiert habe). .

Wenn Sie den Dump öffnen, zeigt Ihnen der erste Bildschirm ein Kreisdiagramm mit den größten Objekten (einschließlich des zurückgehaltenen Heaps) und Sie können schnell zu den Objekten navigieren, die zu groß sind, um bequem zu sein.Es gibt auch eine Funktion zum Suchen nach wahrscheinlichen Leckverdächtigen, die meiner Meinung nach nützlich sein kann, aber da mir die Navigation ausreichte, habe ich mich nicht wirklich darauf eingelassen.

Ein Werkzeug ist eine große Hilfe.

Es gibt jedoch Zeiten, in denen Sie ein Werkzeug nicht verwenden können:Der Heap-Dump ist so groß, dass er das Tool zum Absturz bringt. Sie versuchen, eine Maschine in einer Produktionsumgebung zu reparieren, auf die Sie nur Shell-Zugriff haben usw.

In diesem Fall ist es hilfreich, sich mit der hprof-Dump-Datei vertraut zu machen.

Suchen Sie nach SITES BEGIN.Dies zeigt Ihnen, welche Objekte den meisten Speicher verbrauchen.Aber die Objekte werden nicht nur nach Typ in einen Topf geworfen:Jeder Eintrag enthält außerdem eine „Trace“-ID.Sie können dann nach „TRACE nnnn“ suchen, um die oberen paar Frames des Stapels anzuzeigen, in denen 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 Zuordnungsseite überprüfen und keinen Fehler feststellen, müssen Sie mit der Rückwärtsverkettung von einigen dieser Live-Objekte zu Stammobjekten beginnen, um die unerwartete Referenzkette zu finden.Hier hilft ein Tool wirklich, aber Sie können das Gleiche auch von Hand tun (naja, mit grep).Es gibt nicht nur ein Stammobjekt (d. h. ein Objekt, das nicht der Garbage Collection unterliegt).Threads, Klassen und Stack-Frames fungieren als Stammobjekte und alles, was sie stark referenzieren, ist nicht sammelbar.

Um die Verkettung durchzuführen, suchen Sie im Abschnitt HEAP DUMP nach Einträgen mit der fehlerhaften Trace-ID.Dadurch gelangen Sie zu einem OBJ- oder ARR-Eintrag, der eine eindeutige Objektkennung im Hexadezimalformat anzeigt.Suchen Sie nach allen Vorkommen dieser ID, um herauszufinden, wer einen starken Verweis auf das Objekt hat.Folgen Sie jedem dieser Pfade rückwärts, während sie sich verzweigen, bis Sie herausgefunden haben, wo sich das Leck befindet.Sehen Sie, warum ein Werkzeug so praktisch ist?

Statische Mitglieder sind ein Wiederholungstäter für Speicherlecks.Tatsächlich lohnt es sich auch ohne Tool, ein paar Minuten damit zu verbringen, Ihren Code nach statischen Map-Mitgliedern zu durchsuchen.Kann eine Karte groß werden?Bereinigt irgendetwas jemals seine Einträge?

Meistens ist der angegebene Java-Heap in Unternehmensanwendungen größer als die ideale Größe von maximal 12 bis 16 GB.Es fiel mir schwer, den NetBeans-Profiler direkt auf diesen großen Java-Apps zum Laufen zu bringen.

Normalerweise ist dies jedoch nicht erforderlich.Sie können das mit dem JDK gelieferte Dienstprogramm jmap verwenden, um einen „Live“-Heap-Dump zu erstellen, d. h. jmap speichert den Heap nach der Ausführung von GC.Führen Sie einen Vorgang an der Anwendung aus, warten Sie, bis der Vorgang abgeschlossen ist, und erstellen Sie dann einen weiteren „Live“-Heap-Dump.Verwenden Sie Tools wie Eclipse MAT, um die Heapdumps zu laden, im Histogramm zu sortieren und zu sehen, welche Objekte zugenommen haben oder welche am höchsten sind. Dies würde einen Hinweis geben.

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

Bei diesem Ansatz gibt es nur ein Problem;Riesige Heap-Dumps, selbst mit der Live-Option, sind möglicherweise zu groß, um sie in die Entwicklungsrunde zu übertragen, und erfordern möglicherweise einen Computer mit ausreichend Arbeitsspeicher/RAM zum Öffnen.

Hier kommt das Klassenhistogramm ins Spiel.Mit dem jmap-Tool können Sie ein Live-Klassen-Histogramm sichern.Dadurch wird nur das Klassenhistogramm der Speichernutzung angezeigt. Im Grunde sind nicht die Informationen vorhanden, um die Referenz zu verketten.Beispielsweise kann das Char-Array ganz oben stehen.Und die String-Klasse irgendwo unten.Den Zusammenhang muss man selbst herstellen.

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

Anstatt zwei Heap-Dumps zu erstellen, erstellen Sie zwei Klassenhistogramme, wie oben beschrieben.Vergleichen Sie dann die Klassenhistogramme und sehen Sie, welche Klassen zunehmen.Prüfen Sie, ob Sie die Java-Klassen Ihren Anwendungsklassen zuordnen können.Das wird einen ziemlich guten Hinweis geben.Hier ist ein Python-Skript, das Ihnen beim Vergleich zweier Jmap-Histogramm-Dumps helfen kann. 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 liegt Ihr Problem manchmal vielleicht nicht in einem Speicherverlust, sondern in einer hohen Speichernutzung. Aktivieren Sie dazu die GC-Protokollierung. Verwenden Sie einen fortschrittlicheren und neuen Komprimierungs-GC wie G1GC.und Sie können JDK-Tools wie jstat verwenden, um das GC-Verhalten live zu sehen

jstat -gccause pid <optional time interval>

Weitere Verweise auf Google für -jhat, jmap, Full GC, Humongous Allocation, G1GC

Es gibt Tools, die Ihnen bei der Suche nach Ihrem Leck helfen sollen, wie JProbe, YourKit, AD4J oder JRockit Mission Control.Letzteres kenne ich persönlich am besten.Mit jedem guten Tool sollten Sie einen Drilldown auf eine Ebene durchführen können, auf der Sie leicht erkennen können, welche Lecks vorhanden sind und wo die Leckobjekte zugeordnet sind.

Die Verwendung von HashTables, Hashmaps oder ähnlichem ist eine der wenigen Möglichkeiten, wie Sie in Java überhaupt Speicher verlieren können.Wenn ich das Leck von Hand finden müsste, würde ich regelmäßig die Größe meiner HashMaps ausdrucken und von dort aus diejenige finden, in der ich Elemente hinzufüge und vergesse, sie zu löschen.

Nun, es gibt immer die Low-Tech-Lösung, eine Protokollierung der Größe Ihrer Karten hinzuzufügen, wenn Sie sie ändern, und dann die Protokolle zu durchsuchen, um herauszufinden, welche Karten über eine angemessene Größe hinaus wachsen.

NetBeans verfügt über einen integrierten Profiler.

Sie müssen unbedingt einen Speicherprofiler verwenden, der Zuweisungen verfolgt.Schauen Sie mal rein JProfiler - Ihre „Heap Walker“-Funktion 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 einzelne Lizenz) – Sie werden ziemlich schnell 500 US-Dollar Zeit verschwenden, wenn Sie mit weniger ausgefeilten Tools ein Leck finden müssen.

Vielleicht möchten Sie einen Blick darauf werfen jconsole.Es ist auch Teil des JDK und ich fand es hilfreich, Speicher-/Referenzlecks in Verbindung mit jhat zu finden.Schauen Sie auch mal vorbei Das Blog-Eintrag.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top