Bei der Aufdeckung von JVM -Leistungsproblemen können Sie auf Speicherlecks stoßen und JVM -OutofMemory verursachen. Wenn der Parameter reloadable = "true" bei der Verwendung des Tomcat -Containers eingestellt ist, können Sie beim häufigsten Bereitstellen von Anwendungen auch auf Speicherüberlauf stoßen. Das Prinzip der heißen Bereitstellung von Tomcat besteht darin, festzustellen, dass die Dateien im Verzeichnis web-inf/klassen oder web-inf/lib/lib/lib geändert werden und die Anwendung zuerst gestoppt und dann gestartet wird. Da Tomcat jeder Anwendung standardmäßig einen WebAppclassloader zuweist, besteht das Prinzip des heißen Ersatzes darin, einen neuen Klassenloader zum Laden der Klasse zu erstellen. Da die Einzigartigkeit einer Klasse in der JVM durch ihre Klassendatei und ihren Klassenlader bestimmt wird, kann das Nachladen der Klasse den Zweck des heißen Austauschs erreichen. Wenn die Anzahl der Hot -Bereitstellungen häufiger ist, wird mehr Klassen vom JVM geladen. Wenn die vorherige Klasse aus irgendeinem Grund nicht rechtzeitig entladen wird (z. B. Speicherlecks), kann dies zu einer dauerhaften Erzeugung oder MetaPace -OutofMemory führen. In diesem Artikel wird kurz das Szenario vorgestellt, in dem ThreadLocal und Classloader durch eine Demo zu Speicherlecks und letztendlich ausgeschaltet werden.
Deinstallieren der Klasse
Nachdem die Klasse verwendet wurde, wird es deinstalliert, wenn die folgende Situation erfüllt ist:
1. Alle Instanzen dieser Klasse im Haufen wurden recycelt, dh in den Instanzobjekten dieser Klasse existieren nicht im Haufen.
2. Der Klassenloader, der diese Klasse lädt, wurde recycelt.
3. Auf das Klassenobjekt, das dieser Klasse entspricht, kann man nirgendwo hingewiesen werden, und auf das Klassenobjekt kann nicht durch Reflexion zugegriffen werden.
Wenn die Klasse die Deinstallationsbedingungen erfüllt, deinstalliert der JVM die Klasse, wenn sie in GC ist, dh die Klasseninformationen im Methodenbereich.
Szene Einführung
Im vorherigen Artikel habe ich das Prinzip von ThreadLocal eingeführt. Jeder Thread hat eine ThreadLocalMap. Wenn der Lebenszyklus des Fadens relativ lang ist, kann der Eintrag in ThreadLocalMap nicht recycelt werden. Das ThreadLocal -Objekt wurde immer stark vom Thread verwiesen. Da das Instanzobjekt die Referenz des Klassenobjekts enthält, hält das Klassenobjekt die Referenz des Klassenloaders, der es lädt, wodurch die Klasse entladen wird. Wenn genügend Klassen geladen sind, kann eine dauerhafte Generation oder ein Metaspace -Speicherüberlauf auftreten. Wenn die Klasse über große Objekte wie ein größeres Byte -Array verfügt, wird der Speicherüberlauf des Java -Haufensbereichs verursacht.
Quellcode Einführung
Hier ist eine interne Klasse innerlich. Die innere Klasse verfügt über ein statisches ThreadLocal -Objekt, das hauptsächlich verwendet wird, um Threads stark auf die innere Klasse zu verweisen, so dass die innere Klasse nicht recycelt werden kann. Ein benutzerdefinierter Klassenloader wird definiert, um die innere Klasse zu laden, wie unten gezeigt:
public Class MemoryLeak {public static void main (String [] args) {// Weil der Thread ständig ausgeführt wird, wurde das innere Objekt in ThreadLocalMap stark referenziert durch New Thread (New Runnable () {@Override public void run () {wob ("Load1", MemoryLeak.class.getClassloader (), Com.ezlippi.Memoryleak $ Inner "," Com.ezlippi.Memoryleak $ Inner $ 1 "); GC als Referenzverarbeitung Innere Class = NULL; } //, um den Haufen Bereich schneller öffentlicher statischer Klassen innen zu erreichen. static ThreadLocal <Nern> ThreadLocal = new ThreadLocal <NeNer> () {@Override Protected InnitialValue () {return New Inner (); }}; // threadLocal.get () aufzurufen, um ein inneres Objekt static {threadLocal.get () zu initialisieren; } public inner () {}} // Quellcode weglassen private statische Klasse CustomClassloader erweitert den Classloader {}Überlauf des Haufensbereichs Speicher
Um den Heap -Speicherüberlauf auszulösen, habe ich in der inneren Klasse ein 1 -MB -Byte -Array eingerichtet, und gleichzeitig muss ich ThreadLocal.get () im statischen Block aufrufen. Nur der Aufruf löst initialValue () ein, um ein inneres Objekt zu initialisieren. Andernfalls erstelle ich nur ein leeres ThreadLocal -Objekt, und es gibt keine Daten in der ThreadLocalMap.
Die JVM -Parameter sind wie folgt:
-Xms100m -xmx100m -xx:+useParNewgc -xx:+useConcmarksweepgc -xx:+printgcDetails -xx:+printheapatgc -xx:+printclassHistogramm -xx:+heapdumponoutofmemoryErrorrorrorrorrorrorrorrorrorrorrorrorRorror:+
Nach den letzten 814 Ausführungen überläuft der JVM Heap -Bereichspeicher, wie unten gezeigt:
java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid11824.hprof ...Heap dump file created [100661202 bytes in 1.501 secs]Heap par new generation total 30720K, used 30389K [0x000000000f9c000000, 0x00000000fbd500000) eden space 27328K, 99% verwendet [0x00000000f9c00000000, 0x00000000fb6ad450, 0x00000000fb6b0000) aus Space 3392K, 90% verwendet [0x000000000000fb3b000000, 0x00000000fb0030, 0x00000000000 000000 000000000 000000000 000000000 000000 000000 000000 000000 000000) bis 3392K, 0% ige. [0x0000000000fba00000, 0x00000000fbd500000) concurrent mark-sweep generation total 68288K, used 67600K [0x000000000fbd500000, 0x0000001000000000, 0x000000010000000) Metaspace used 3770K, capacity 5134K, committed 5248K, reserved 1056768K class Space Nutzung 474K, Kapazität 578K, verpflichtet 640K, reserviert 1048576Kexception in Thread "Thread-0" java.lang.outofMemoryError: Java Heap Space bei com.ezlippi.Memoryleak $ Inner. <Clinit> (maitemleak.java:34) at ATREAT. sun.reflect.nativeconstructorAccessorimpl.Newinstance0 (native Methode) bei sun.reflect.nativeconstructorAccessorImpl.Newinstance (unbekannte Quelle) bei sun.reflect.delegatingConstrucessorimpl.newinstance (unbekannte Quelle) in Java. java.lang.reflect.constructor.newinstance (unbekannte Quelle) Quelle) unter java.lang.class.newinstance (unbekannte Quelle) unter com.ezlippi.memoryleak $ 1.run (majoreLeak.java:20) bei java.lang.thread.run (Unbekannte Quelle) (Unbekannte Quelle) (Unbekannte Quelle) (Unbekannte Quelle) (Unbekannte Quelle) (Unbekannte Quelle) (unbekannt)
Sie können sehen, dass das JVM keinen Speicher hat, um ein neues inneres Objekt zu erstellen, da der Haufen Bereich viele 1 -MB -Byte -Arrays speichert. Hier druckte ich das Histogramm der Klasse aus (die folgende Abbildung ist eine Szene mit einer Haufengröße von 1024 m), in der einige unbedeutende Klassen weggelassen werden. Es ist ersichtlich, dass das Byte -Array 855 m Platz nimmt und 814 Instanzen von com.ezlippi.memoryleak $ customClassloader erstellt werden, die im Grunde der Größe des Byte -Arrays übereinstimmen:
Num #Instances #Bytes Klasse Name------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- com.ezlippi.memoryleak $ CustomClassloader 12: 820 53088 [ljava.util.hashtable $ Eintrag; 15: 817 39216 java.util.hashtable 16: 915 36600 Java.lang.Ref.softreference 17: 543 34752 Java.net.url 18: 697 33456 Java.Nio.Heapcharbuffer 19: 817 32680 Java.Security.Protetiondomain 19: 817 32680 Java. Java.util.Treemap $ Eintrag 21: 928 29696 java.util.hashtable $ Eintrag 22: 1802 28832 Java.util
Metaspace -Überlauf
Um den Überlauf von Metaspace zu machen, müssen Sie den Raum von Metaspace ein wenig reduzieren und genügend Klassen vor dem Haufen überlaufen laden. Daher habe ich die JVM -Parameter eingestellt und die Größe des Byte -Arrays auf 1 KB eingestellt, wie unten gezeigt:
private byte[] KB = new byte[1024];-Xms100m -Xmx100m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintClassHistogram -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m
Aus dem GC -Protokoll können wir erkennen, dass FullGC ausgelöst wird, wenn der Meraspace den GC -Schwellenwert erreicht (dh die Größe der MaxMetaspaceSize -Konfiguration):
java.lang.OutOfMemoryError: Metaspace <<no stack trace available>>{Heap before GC invocations=20 (full 20): par new generation total 30720K, used 0K [0x000000000f9c000000, 0x00000000fbd500000) eden space 27328K, 0% used [0x000000000f9c000000, 0x00000000f9c00000, 0x0000000f9c00000, 0x0000000f9c00000, 0x0000000fb6b0000) aus Space 3392K, 0% verwendet [0x000000000000fb6b000000, 0x00000000Fb00000000000000000) bis Space 3392K, 0% verwendet, 0x00000000000) bis Space 3392K, 0% verwendet. [0x0000000000fba00000, 0x000000000fbd500000) to space 3392K, 0% used [0x0000000000fba00000, 0x000000010000000000) Metaspace used 1806K, capacity 1988K, committed 2048K, reserved 1056768K class space used 202K, capacity 384K, committed 384K, reserved 1048576K [Full GC (Metadaten -GC -Schwellenwert) [CMSProcess mit dem Ausgangscode 1 fertiggestelltAus dem obigen Beispiel können wir sehen, dass der Klassenlader und ThreadLocal nicht ordnungsgemäß verwendet werden, sondern tatsächlich zu Speicherleckage. Der vollständige Quellcode befindet sich in GitHub