Erfassen und analysieren Sie Java-Heap-Dump
- Einführung in Heap Dump und seine Formate
- 
          
            Beispielcode, der OutOfMemoryErrorin Java verursacht
- Verschiedene Möglichkeiten, Heap Dump zu erfassen
- Analysieren Sie den Java-Heap-Dump
 
Heap-Dumps enthalten eine Momentaufnahme aller Live-Objekte, die die laufende Java-Anwendung auf einem Java-Heap verwendet. Dieses Tutorial informiert über Heap-Dump, seine verschiedenen Formate und seine Bedeutung.
Darüber hinaus werden wir ein Beispiel durchgehen, das OutOfMemoryError demonstriert, was zu verschiedenen Ansätzen zur Erfassung von Heap-Dumps und einem Tool zu deren Analyse führen wird.
Einführung in Heap Dump und seine Formate
Ein Heap enthält alle Objekte, die wir durch Instanziieren einer Klasse erstellen. Jede Klasse der Java-Laufzeitumgebung wird ebenfalls in diesem Heap erstellt.
Dieser Heap wird beim Start von JVM (Java Virtual Machine) erstellt und kann während der Laufzeit erweitert/verkleinert werden, um zerstörte oder in einer Anwendung erstellte Objekte anzupassen.
Der Garbage-Collection-Prozess läuft immer dann, wenn ein Heap voll wird, dieser Prozess sammelt alle Objekte, die nicht mehr verwendet werden, oder wir können sagen, nicht mehr referenziert werden (Sie können mehr über Speicherverwaltung hier finden). Normalerweise werden Heap-Dumps in der hprof-Dateien im Binärformat.
Wir können detaillierte Informationen über jede Objektinstanz abrufen, wie Typ, Klassenname, Adresse, Größe und ob eine Instanz Verweise auf andere Objekte enthält oder nicht. Die Heap-Dumps können in einem der folgenden zwei Formate vorliegen:
- Das Portable Heap Dump Format (auch bekannt als PHD-Format)
- Das klassische Format
Denken Sie daran, dass der portable Heap-Dump das Standardformat ist und im Binärformat vorliegt, das für die weitere Analyse verarbeitet werden muss. Andererseits ist das klassische Format ASCII-Text, der für Menschen lesbar ist.
Sie können über diese beiden Formate hier lesen.
Bedeutung der Verwendung von Heap Dump
Normalerweise erhalten wir den Vorteil des Heap-Dumps, wenn eine Anwendung aufgrund von OutOfMemoryError abstürzt oder eine Java-Anwendung mehr Speicher verbraucht als erwartet.
Heap-Dump hilft uns, die Hauptursachen für den Fehler und andere Details zu identifizieren, z. B. die Anzahl der Objekte in jeder Klasse, die Speichernutzung für jede Klasse usw.
Es hilft auch bei der Erfassung der Speichergröße, die von jedem Java-Objekt einer Anwendung belegt wird. All diese erfassten Informationen können nützlich sein, um einen tatsächlichen Code zu finden, der Probleme mit Speicherlecks verursacht.
Schauen wir uns ein Codebeispiel an, das OutOfMemoryError verursacht, was zu verschiedenen Möglichkeiten führt, einen Java-Heap-Dump zu erfassen.
Beispielcode, der OutOfMemoryError in Java verursacht
    
import java.util.ArrayList;
import java.util.List;
public class Test {
  public static void main(String[] args) {
    List<byte[]> myList = new ArrayList<>();
    int index = 0;
    while (true) {
      // 1MB each iteration, 1 x 1024 x 1024 = 1048576
      byte[] bytes = new byte[1048576];
      myList.add(bytes);
      Runtime runTime = Runtime.getRuntime();
      System.out.printf("[%d] free memory is: %s%n", index++, runTime.freeMemory());
    }
  }
}
Der obige Code weist weiterhin Speicher zu, indem er die while-Schleife ausführt, bis ein bestimmter Punkt erreicht ist, an dem Java Virtual Machine nicht genügend Speicher zuordnen kann.
An diesem Punkt erhalten wir den Fehler java.lang.OutOfMemoryError: Java heap space.
...
...
...
[1510] free memory is: 14687728
[1511] free memory is: 12590576
[1512] free memory is: 10493424
[1513] free memory is: 8396272
[1514] free memory is: 6299120
[1515] free memory is: 4201968
[1516] free memory is: 2104320
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at Test.main(Test.java:16)
Hier müssen wir eine Heap-Dump-Analyse durchführen, um die Gründe für OutOfMemoryError zu finden. Dies kann in zwei Schritten erfolgen.
Erfassen Sie zuerst den Heap-Dump und analysieren Sie dann die Heap-Dump-Datei, um die vermuteten Gründe zu finden.
Verschiedene Möglichkeiten, Heap Dump zu erfassen
Es gibt mehrere Möglichkeiten zum Erfassen von Heap-Dumps. Lassen Sie uns das Folgende nacheinander lernen.
- jmap
- JVisualVM
- jcmd
- Generieren Sie Heap-Dump automatisch
- JMX
Verwenden Sie jmap, um Heap Dump zu erfassen
Das Tool jmap druckt Speicherstatistiken in einer laufenden Java Virtual Machine (JVM); Wir können es auch für entfernte und lokale Prozesse verwenden.
Wir verwenden die Option -dump, um den Heap-Dump mit dem Tool jmap zu erfassen, das wir aus dem Ordner bin des JDK-Home-Verzeichnisses verwenden können.
Syntax:
jmap -dump:[live],format=b,file=<file-path> <pid>
Beispiel:
C:\Program Files\Java\jdk-18\bin> jmap -dump:live,format=b,file=/temp/dump.hprof 12876
Im Folgenden finden Sie eine kurze Beschreibung der Optionen, die wir oben angegeben haben:
| Parameter | Beschreibung | 
|---|---|
| live | Dieser Parameter ist optional; Wenn es gesetzt ist, werden nur Objekte mit aktiven Referenzen gedruckt. Es lässt diejenigen weg, die für die Garbage Collection bereit sind. | 
| format=b | Es wird verwendet, um das Format anzugeben, in dem die Dump-Datei gespeichert wird. Hier haben wir bverwendet, was bedeutet, dass diese Dump-Datei im Binärformat vorliegt. Das Ergebnis ist dasselbe, wenn dieser Parameter nicht gesetzt ist. | 
| file | Es ist die Datei, in die der Dump geschrieben wird. | 
| pid | Es bezeichnet eine ID des Java-Prozesses. | 
Beachten Sie, dass wir den Befehl jps verwenden können, um pid zu erhalten. Zusätzlich wurde jmap als experimentelles Tool in JDK eingeführt und wird nicht unterstützt; daher müssen Sie in manchen Situationen statt jmap auf andere Tools zurückgreifen.
Verwenden Sie JVisualVM, um Heap Dump zu erfassen
Die JVisualVM ist eine grafische Benutzeroberfläche, mit der wir die Profilerstellung von Java-Anwendungen und die Fehlerbehebung verfolgen können. Es ist einfach, leicht zu bedienen und lässt uns einen Heap-Dump erfassen.
Laut this war es mit Oracle JDK 6, 7 und 8 verfügbar. Ab JDK 9 oder höher wird JVisualVM nicht mehr mit Oracle JDK ausgeliefert; die Benutzer müssen es separat herunterladen, wenn sie es verwenden möchten.
Laden wir es von visualvm.github.io herunter, extrahieren Sie die .zip-Datei, suchen Sie visualvm.exe im Ordner bin, doppelklicken Sie darauf, klicken Sie auf die Schaltfläche I Accept, wenn Sie dazu aufgefordert werden, und Sie werden den folgenden Bildschirm sehen.

Unter Lokal werden alle aktuell laufenden Java-Prozesse aufgelistet. Wir können einen Heap Dump erfassen, indem wir den gewünschten Java-Prozess auswählen, mit der rechten Maustaste darauf klicken und die Option Heap Dump wählen.
Es öffnet sich eine neue Registerkarte mit allen notwendigen Informationen.
Verwenden Sie jcmd, um Heap Dump zu erfassen
Dieses Tool befindet sich auch im Ordner bin des Home-Verzeichnisses von JDK; in unserem Fall ist es C:\Program Files\Java\jdk-18\bin. Ihre könnte anders sein, wenn Sie Java nicht am Standardspeicherort installiert haben.
Der jcmd sendet Befehlsanfragen an eine Java Virtual Machine. Denken Sie daran, dass wir es auf derselben Maschine verwenden müssen, auf der ein Java-Prozess ausgeführt wird.
Es hat mehrere Befehle, einer davon ist GC.heap-dump, mit denen wir einen Heap-Dump erfassen, indem wir die Prozess-ID (pid) und einen Pfad für eine Ausgabedatei angeben. Siehe die Syntax des Befehls jcmd unten.
Syntax:
jcmd <pid> GC.head_dump <file-path>
Beispiel:
C:\Program Files\Java\jdk-18\bin> jcmd 12876 GC.head_dump /temp/dump.hprof
Wie jmap generiert es auch den Dump im Binärformat.
Heap-Dump automatisch erfassen
Alle Ansätze, die wir gelernt haben, erfassen den Heap-Dump manuell zu einem bestimmten Zeitpunkt, aber unter Umständen müssen wir einen Heap-Dump erzeugen, sobald java.lang.OutOfMemoryError auftritt.
Hier hilft uns das automatische Generieren eines Heap-Dumps, einen Fehler zu untersuchen.
In Anbetracht dieser Szenarien dient Java mit HeapDumpOnOutOfMemoryError , einer Befehlszeilenoption, die einen Heap-Dump erzeugen kann, wenn eine Anwendung java.lang.OutOfMemoryError auslöst.
java -XX:+HeapDumpOnOutOfMemoryError
Standardmäßig speichert der obige Befehl den Dump in der Datei java_pid<pid>.hprof, die sich dort befindet, wo unsere Anwendung ausgeführt wird. Wir können ein benutzerdefiniertes Verzeichnis oder eine benutzerdefinierte Datei angeben und in einer HeapDumpPath-Option festlegen.
Syntax:
java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=<file-or-dir-path>
Beispiel:
java -XX:+HeapDumpOnOutOfMemoryError  -XX:HeapDumpPath=/temp/heapdump.bin
Jetzt können wir die erstellte Datei mit dem Heap-Dump wie folgt in Protokollen finden, wenn unserer Anwendung über diese Option der Speicher ausgeht.
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
During heap to java_pid12876.hprof...
  Exception in thread "main" Head dump file created [4745371 bytes in 0.028 secs]
  java.lang.OutOfMemoryError: Requested array size exceeds VM limit
Wir können sehen, dass der obige Text in die Datei java_pid12876.hprof geschrieben wird, und es gibt keinen Overhead, wenn unsere Anwendung mit dieser Option ausgeführt wird.
Es ist gut, diese Option für alle Anwendungen zu verwenden, insbesondere in der Produktion, da Sie nie wissen, wann ein OutOfMemoryError auftritt.
Denken Sie daran, dass wir diese Option zur Laufzeit mit HotSpotDiagnostic MBean verwenden können. Dazu wird JConsole verwendet und die VM-Option HeapDumpOnOutOfMemoryError auf true gesetzt.
Verwenden Sie JMX, um Heap Dump zu erfassen
In dieser Methode verwenden wir HotSpotDiagnostic MBean, das eine dumpHeap-Methode bereitstellt, die die folgenden zwei Parameter akzeptiert:
| Parameter | Beschreibung | 
|---|---|
| outputFile | Es ist ein Pfad einer Ausgabedatei für dump; diese Datei muss die Erweiterung .hprofhaben, um Dump zu speichern. | 
| live | Wenn wir es auf truesetzen, werden nur die aktiven Objekte im Speicher ausgegeben, wie wir bei der Verwendung vonjmapin diesem Tutorial gelernt haben. | 
Wir können es auf zwei Arten aufrufen, um einen Heap-Dump zu erfassen, es programmgesteuert aufrufen oder den JMX-Client wie JConsole verwenden, der sich im bin-Ordner des JDK-Home-Verzeichnisses befindet. Wir werden hier JMX verwenden, aber Sie können hier lernen, wie man es programmgesteuert aufruft.
Die Verwendung von HotSpotDiagnostic MBean über den JMX-Client (JConsole) ist der einfachste Weg, um JConsole zu öffnen, sich mit dem laufenden Java-Prozess zu verbinden, zur Registerkarte MBeans zu navigieren und unter com .Sonnenmanagement.
Wir finden die Methode dumpHeap unter der Dropdown-Liste Operationen, wo wir die Parameter outputFile und live in den Textfeldern p0 und p1 angeben können, um die Operation dumpHeap wie unten gezeigt auszuführen.

Jetzt ist es an der Zeit, den Java-Heap-Dump zu analysieren.
Analysieren Sie den Java-Heap-Dump
Im Java-Heap-Dump müssen wir nach Objekten mit hohem Speicher, Objektgraphen zum Auffinden von Objekten, die keinen Speicher freigeben und erreichbaren und nicht erreichbaren Objekten suchen.
Der Eclipse Memory Analyzer (MAT) eignet sich am besten zum Analysieren des Java-Heap-Dumps, den wir zuvor generiert haben. Wir starten das Memory Analyzer Tool und öffnen dazu eine Heap-Dump-Datei.
In Eclipse Memory Analyzer (MAT) haben wir zwei Arten von Objektgrößen, die im Folgenden kurz erläutert werden.
- Shallow Heap Size– Der flache Heap eines Objekts ist seine Größe im Speicher.
- Retained Heap Size– Es ist eine Speichermenge, die freigegeben wird, sobald ein Objekt durch Garbage Collection erfasst wird.
Sobald die Heap-Dump-Datei geöffnet ist, können wir eine Zusammenfassung der Speichernutzung einer Anwendung sehen. Jetzt können wir leicht herausfinden, was OutOfMemoryError verursacht.
