So finden Sie ein Java-Speicherleck

Lesezeit: 10 Minuten

So finden Sie ein Java Speicherleck
jwiklund

Wie finden Sie ein Speicherleck in Java (z. B. mit JHat)? Ich habe versucht, den Heap-Dump in JHat zu laden, um einen grundlegenden Blick darauf zu werfen. Ich verstehe jedoch nicht, wie ich die Root-Referenz (Ref) oder wie auch immer sie heißt, finden soll. Grundsätzlich kann ich sagen, dass es mehrere hundert Megabyte an Hash-Tabelleneinträgen gibt ([java.util.HashMap$Entry or something like that), but maps are used all over the place… Is there some way to search for large maps, or perhaps find general roots of large object trees?

[Edit]
Ok, ich habe die Antworten bisher gelesen, aber sagen wir einfach, ich bin ein billiger Bastard (was bedeutet, dass ich mehr daran interessiert bin, zu lernen, wie man JHat benutzt, 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 bei JHat keinen anderen Weg als rohe Gewalt, aber ich kann nicht glauben, dass das der Fall sein kann.

Außerdem glaube ich nicht, dass ich in der Lage sein werde, tatsächlich Änderungen vorzunehmen (Hinzufügen der Protokollierung von alle Kartengrößen) und lass es lange genug laufen, damit ich das Leck bemerke.

  • Dies ist eine weitere “Abstimmung” für JProfiler. Es funktioniert ziemlich gut für die Heap-Analyse, hat eine anständige Benutzeroberfläche und funktioniert ziemlich gut. Wie McKenzieG1 sagt, sind 500 US-Dollar billiger als die Zeit, die Sie sonst für die Suche nach der Quelle dieser Lecks aufwenden würden. Soweit der Preis für Werkzeuge gehen, ist es nicht schlecht.

    – Joev

    2. September 2008 um 18:08 Uhr

  • Oracle hat hier eine relevante Seite: docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/…

    – Lorbeer

    28. Mai 2016 um 20:26 Uhr

Ich verwende den folgenden Ansatz, um Speicherlecks in Java zu finden. Ich habe jProfiler mit großem Erfolg verwendet, aber ich glaube, dass jedes spezialisierte Tool mit Grafikfunktionen (Diffs sind in grafischer Form einfacher zu 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 im Leerlauf ist.
  2. Führen Sie den Vorgang, bei dem ein Speicherleck vermutet wird, mehrmals aus, damit Cache- und DB-bezogene Initialisierungen stattfinden können.
  3. Führen Sie GC aus und erstellen Sie einen Speicherschnappschuss.
  4. Führen Sie den Vorgang erneut aus. Abhängig von der Komplexität des Vorgangs und der Größe der zu verarbeitenden Daten muss der Vorgang möglicherweise mehrere bis viele Male ausgeführt werden.
  5. Führen Sie GC aus und erstellen Sie einen Speicherschnappschuss.
  6. Führen Sie einen Diff für 2 Snapshots aus 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 bleiben.

Für Webanwendungen, die Anfragen in mehreren Threads verarbeiten, wird die Analyse komplizierter, aber dennoch gilt 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.

  • Die meisten (wenn nicht alle) Java-Profiler bieten Ihnen die Möglichkeit, GC mit einem Klick auf eine Schaltfläche aufzurufen. Oder Sie können System.gc() an der entsprechenden Stelle in Ihrem Code aufrufen.

    – Dima Malenko

    4. Januar 2010 um 19:13 Uhr

  • Selbst wenn wir System.gc() aufrufen, kann JVM den Aufruf vernachlässigen. AFAIK ist dies JVM-spezifisch. +1 auf die Antwort.

    – Aniket Thakur

    27. Dezember 2013 um 9:19 Uhr


  • Was genau ist ein „Erinnerungsschnappschuss“? Gibt es etwas, das mir die Nummer jedes Objekttyps mitteilt, den mein Code ausführt?

    – Gnom

    22. Mai 2015 um 16:38 Uhr

  • Wie gehe ich von “beginne mit dem größten positiven Unterschied nach Objekttypen” zu “finde, was dazu führt, dass diese zusätzlichen Objekte im Speicher bleiben”? Ich sehe sehr allgemeine Dinge wie int[]Objekt[]String usw. Wie finde ich heraus, woher sie kommen?

    – Vituel

    19. April 2017 um 14:49 Uhr

So finden Sie ein Java Speicherleck
jwiklund

Fragender hier, ich muss sagen, dass es viel einfacher ist, potenzielle Speicherlecks zu finden, wenn Sie ein Tool haben, das keine 5 Minuten braucht, um auf einen Klick zu antworten.

Da die Leute mehrere Tools vorschlagen (ich habe nur Visual Wm ausprobiert, seit ich das in der JDK- und JProbe-Testversion bekommen habe), sollte ich ein kostenloses / Open-Source-Tool vorschlagen, das auf der Eclipse-Plattform basiert, dem Memory Analyzer (manchmal als SAP-Speicher bezeichnet). Analysator) verfügbar auf http://www.eclipse.org/mat/ .

Was wirklich cool an diesem Tool ist, ist, dass es den Heap-Dump indiziert hat, als ich es zum ersten Mal geöffnet habe, wodurch es Daten wie den aufbewahrten Heap anzeigen konnte, ohne 5 Minuten auf jedes Objekt warten zu müssen (so ziemlich alle Operationen waren tonnenweise schneller als die anderen Tools, die ich ausprobiert habe). .

Wenn Sie den Dump öffnen, zeigt Ihnen der erste Bildschirm ein Tortendiagramm mit den größten Objekten (Zählung des zurückbehaltenen Haufens) und Sie können schnell zu den Objekten navigieren, die zu groß für den Komfort sind. Es hat auch eine Möglichkeit, wahrscheinliche Leckverdächtige zu finden, was meiner Meinung nach nützlich sein kann, aber da mir die Navigation ausgereicht hat, habe ich mich nicht wirklich damit befasst.

  • Bemerkenswert: anscheinend in Java 5 und höher, die HeapDumpOnCtrlBreak VM-Parameter ist nicht verfügbar. Die Lösung, die ich gefunden habe (bisher noch auf der Suche), besteht darin, JMap zu verwenden, um die .hprof Datei, die ich dann in Eclipse einfüge und mit MAT untersuche.

    – Ben

    11. Januar 2012 um 5:38 Uhr

  • In Bezug auf das Erhalten des Heap-Dumps enthalten die meisten Profiler (einschließlich JVisualVM) die Option, sowohl Heap als auch Threads in eine Datei zu sichern.

    – bbaja42

    11. Januar 2012 um 20:51 Uhr

Ein Werkzeug ist eine große Hilfe.

Es gibt jedoch Zeiten, in denen Sie ein Tool nicht verwenden können: Der Heap-Dump ist so groß, dass es das Tool zum Absturz bringt, Sie versuchen, Fehler auf einem Computer 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 verwenden. Aber die Objekte werden nicht nur nach Typ in einen Topf geworfen: Jeder Eintrag enthält auch eine „Trace“-ID. Sie können dann nach diesem “TRACE nnnn” suchen, um die obersten Frames des Stapels anzuzeigen, denen das Objekt zugewiesen wurde. Wenn ich sehe, wo das Objekt zugewiesen ist, finde ich oft einen Fehler und bin fertig. Beachten Sie auch, dass Sie mit den Optionen für -Xrunhprof steuern können, wie viele Frames im Stack aufgezeichnet werden.

Wenn Sie sich die Zuordnungssite ansehen und nichts Falsches sehen, müssen Sie mit der Rückwärtsverkettung von einigen dieser Live-Objekte zu Stammobjekten beginnen, um die unerwartete Verweiskette zu finden. Hier hilft ein Tool wirklich, aber Sie können dasselbe auch von Hand machen (na ja, mit grep). Es gibt nicht nur ein Root-Objekt (dh Objekt, das nicht der Garbage-Collection unterliegt). Threads, Klassen und Stapelrahmen 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 Ablaufverfolgungs-ID. Dadurch gelangen Sie zu einem OBJ- oder ARR-Eintrag, der eine eindeutige Objektkennung in Hexadezimalform 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, wenn sie sich verzweigen, bis Sie herausfinden, wo sich das Leck befindet. Sehen Sie, warum ein Tool so praktisch ist?

Statische Mitglieder sind Wiederholungstäter für Speicherlecks. Auch ohne ein Tool lohnt es sich, 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?

  • „Der Heap-Dump ist so groß, dass das Tool abstürzt“ – zuletzt habe ich überprüft, jhat und MAT versuchen anscheinend, den gesamten Heap-Dump in den Speicher zu laden, und stürzen daher normalerweise mit einem ab OutOfMemoryError auf großen Speicherauszügen (dh von den Anwendungen, die am meisten Heap-Analysen benötigten!). Der NetBeans-Profiler scheint einen anderen Algorithmus zum Indizieren von Verweisen zu verwenden, die abgerufen werden können schleppend bei großen Dumps verbraucht aber zumindest nicht unbegrenzt Speicher im Tool und stürzt ab.

    – Jesse Glick

    12. August 2016 um 13:34 Uhr

So finden Sie ein Java Speicherleck
Alex Punnen

In Unternehmensanwendungen ist der angegebene Java-Heap meist größer als die ideale Größe von maximal 12 bis 16 GB. Ich fand es schwierig, den NetBeans-Profiler direkt auf diesen großen Java-Apps zum Laufen zu bringen.

Aber normalerweise ist dies nicht erforderlich. Sie können das jmap-Dienstprogramm verwenden, das mit jdk geliefert wird, um einen “Live” -Heap-Dump zu erstellen, d. h. jmap wird den Heap nach dem Ausführen von GC sichern. Führen Sie eine Operation an der Anwendung durch, warten Sie, bis die Operation abgeschlossen ist, und nehmen Sie dann einen weiteren “Live” -Heap-Dump vor. Verwenden Sie Tools wie Eclipse MAT, um die Heapdumps zu laden, sortieren Sie das Histogramm, sehen Sie, welche Objekte zugenommen haben oder welche die 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 können selbst mit der Live-Option zu groß sein, um sie in die Entwicklungsrunde zu übertragen, und erfordern möglicherweise einen Computer mit genügend Speicher/RAM zum Öffnen.

Hier kommt das Klassenhistogramm ins Bild. Sie können ein Live-Klassenhistogramm mit dem jmap-Tool ausgeben. Dies gibt nur das Klassenhistogramm der Speichernutzung aus. 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 darunter. Die Verbindung müssen Sie selbst herstellen.

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

Anstatt zwei Heap-Dumps zu nehmen, nehmen Sie zwei Klassenhistogramme, wie oben beschrieben; Vergleichen Sie dann die Klassenhistogramme und sehen Sie die Klassen, die zunehmen. Prü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 sehen und festzustellen, ob ein Speicherleck vorliegt. Schließlich ist Ihr Problem manchmal kein Speicherleck, sondern eine hohe Speicherauslastung. Aktivieren Sie dazu die GC-Protokollierung. Verwenden Sie eine fortschrittlichere und neue 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>

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

Es gibt Tools, die Ihnen helfen sollten, Ihr Leck zu finden, wie JProbe, YourKit, AD4J oder JRockit Mission Control. Letztere kenne ich persönlich am besten. Jedes gute Tool sollte Sie bis zu einer Ebene aufschlüsseln lassen, auf der Sie leicht erkennen können, welche Lecks und wo die undichten Objekte zugeordnet sind.

Die Verwendung von HashTables, Hashmaps oder ähnlichem ist eine der wenigen Möglichkeiten, wie Sie überhaupt Speicher in Java lecken 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.

So finden Sie ein Java Speicherleck
Mike Stein

Nun, es gibt immer die Low-Tech-Lösung, die Größe Ihrer Karten zu protokollieren, wenn Sie sie ändern, und dann in den Protokollen nach Karten zu suchen, die über eine angemessene Größe hinauswachsen.

1646322437 812 So finden Sie ein Java Speicherleck
wbkang

NetBeans hat einen eingebauten Profiler.

924920cookie-checkSo finden Sie ein Java-Speicherleck

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy