Erfassen und analysieren Sie Java-Heap-Dump
- Einführung in Heap Dump und seine Formate
-
Beispielcode, der
OutOfMemoryError
in 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 b verwendet, 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 .hprof haben, um Dump zu speichern. |
live |
Wenn wir es auf true setzen, werden nur die aktiven Objekte im Speicher ausgegeben, wie wir bei der Verwendung von jmap in 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.