JVM interner Strukturdiagramm
Java -virtuelle Maschinen sind hauptsächlich in fünf Bereiche unterteilt: Methodenbereich, Haufen, Java -Stack, PC -Register und lokaler Methodenstapel. Schauen wir uns einige wichtige Themen zur JVM -Struktur an.
1. Welche Bereiche werden geteilt? Welche sind privat?
Der Java -Stack, der lokale Methode -Stack und der Programmschalter werden festgelegt und zerstört, wenn der Benutzer -Thread startet und endet.
Jeder Thread hat diese unabhängigen Bereiche. Der Methodenbereich und der Haufen werden im gesamten JVM -Prozess von allen Threads geteilt.
2. Was wird im Methodenbereich gespeichert? Wird es recycelt?
Der Methodenbereich ist nicht nur gespeicherte Methodeninformationen und Code. Gleichzeitig in einer Subregion, die als Laufzeit konstanter Pool bezeichnet wird, werden verschiedene symbolische Referenzen in der konstanten Tabelle in der Klassendatei sowie übersetzte direkte Referenzen übersetzt. Diese Informationen werden über ein Klassenobjekt im Heap als Schnittstelle zugegriffen.
Obwohl die Typinformationen im Methodenbereich gespeichert sind, wird sie auch recycelt, die Recyclingbedingungen sind jedoch relativ streng:
(1) Alle Fälle dieser Klasse wurden recycelt
(2) Der Klassenloader, der diese Klasse lädt
(3) Das Klassenobjekt dieser Klasse wird nirgendwo referenziert (einschließlich Class.Forname Reflection Access)
3. Ist der Inhalt des konstanten Pools im Methodenbereich unverändert?
Der konstante Laufzeitpool im Methodenbereich speichert die Daten im statischen konstanten Pool in der Klassendatei. Zusätzlich zur Speicherung verschiedener wörtlicher und symbolischer Referenzen, die zur Kompilierungszeit erzeugt wurden, enthält es auch übersetzte direkte Referenzen. Dies bedeutet jedoch nicht, dass sich der konstante Pool während der Laufzeit nicht ändert. Wenn Sie beispielsweise beim Ausführen die Praktikum der String -Praktikum aufrufen und neue String -Konstanten in den Pool einfügen.
Paket com.cdai.jvm; public class runTimeConstantpool {public static void main (String [] args) {String s1 = new String ("Hallo"); String s2 = new String ("Hallo"); System.out.println ("vor Praktikum, S1 == S2:" + (S1 == S2)); s1 = s1.intern (); s2 = s2.intern (); System.out.println ("After Intern, S1 == S2:" + (S1 == S2)); }}
4. Werden alle Objektinstanzen auf dem Haufen zugewiesen?
Mit der allmählichen Reife der Escape-Analyse-Technologie haben die Technologien zur Allokation und der skalaren Ersatzoptimierung "alle auf dem Haufen zugewiesenen Objekte weniger absolut gemacht.
Die sogenannte Flucht bedeutet, dass, wenn der Zeiger eines Objekts mit mehreren Methoden oder Threads verwiesen wird, dass dieser Zeiger entkommt.
Im Allgemeinen werden Java -Objekte im Haufen zugewiesen, und nur die Zeiger des Objekts werden im Stapel gespeichert. Unter der Annahme, dass eine lokale Variable während der Ausführung der Methode (außerhalb der Methode ausgesetzt) nicht entkommt, wird sie direkt auf dem Stapel zugewiesen und dann weiterhin im Anrufstapel ausgeführt. Nach der Ausführung der Methode wird der Stapelraum recycelt und die lokalen Variablen werden ebenfalls recycelt. Dies reduziert die Zuweisung einer großen Anzahl temporärer Objekte im Haufen und verbessert die Effizienz des GC -Recyclings.
Darüber hinaus werden die Fluchtanalyse auch Sperrungen für lokale Variablen weglassen, die nicht entkommen sind, und Schlösser aus dieser Variablen auszulassen.
Fügen Sie bei der Aktivierung der Fluchtanalysemethode JVM -Startparameter hinzu: -xx: +capapeanalyse? Escapeanalysistest.
5. Wie viele Möglichkeiten gibt es, um auf Objekte auf dem Haufen zugreifen zu können?
(1) Direkter Zugriff auf Zeiger
Die Referenz auf den Stapel speichert einen Zeiger auf ein Objekt auf dem Haufen, und Sie können das Objekt auf einmal finden, das eine schnellere Zugriffsgeschwindigkeit aufweist.
Wenn jedoch Objekte in den Haufen bewegt werden (jede Objekte werden häufig während der Müllsammlung bewegt), muss auch der Wert der Zeigervariablen auf dem Stapel geändert werden. Derzeit nimmt JVM Hotspot diese Methode an.
(2) indirekten Zugriff auf das Handle
Die Referenz auf den Stapel verweist auf einen Griff im Griffpool, und auf das Objekt wird durch den Wert in diesem Griff zugegriffen. Daher ist der Griff wie ein sekundärer Zeiger, der zwei Positionierung erfordert, um auf das Objekt zuzugreifen, das langsamer ist als die direkte Positionierung des direkten Zeigers. Wenn sich das Objekt jedoch im Haufen bewegt, müssen der auf dem Stapel verwiesene Wert nicht geändert werden.
Wie man JVM überfließt
Nachdem wir die Rolle der fünf Speicherbereiche von Java -Virtual -Maschinen verstanden haben, lernen wir unter den Umständen weiter, unter welchen Umständen diese Bereiche überlaufen werden.
1. Konfiguration Virtual Machine Parameter
-Xms: Die anfängliche Heap -Größe ist standardmäßig 1/64 des physischen Speichers (<1 GB); Standard (minheapfreeratio -Parameter kann eingestellt werden) Wenn der freie Heap -Speicher weniger als 40%beträgt, erhöht der JVM den Haufen bis zur maximalen Grenze von -xmx.
-Xmx: Maximale Haufengröße, Standard (MaxheapFreeratio -Parameter kann eingestellt werden) Wenn der freie Heap -Speicher mehr als 70%beträgt, reduziert der JVM den Haufen, bis die Mindestgrenze von -xms.
-XSS: Stapelgröße für jeden Thread. Nach JDK5.0 beträgt die Stapelgröße jedes Fadens 1 m, und in der Vergangenheit beträgt die Stapelgröße jedes Fadens 256K. Es sollte entsprechend der erforderlichen Speichergröße des Anwendungs -Threads entsprechend eingestellt werden. Im selben physischen Speicher kann die Reduzierung dieses Wertes mehr Threads erzeugen. Das Betriebssystem hat jedoch immer noch eine Grenze für die Anzahl der Threads in einem Prozess und kann nicht unendlich erzeugt werden, wobei die Erfahrungwerte zwischen etwa 3000 bis 5000 reichen. Im Allgemeinen sollte kleine Anwendungen, wenn der Stapel nicht sehr tief ist, 128K ausreichen. Für große Anwendungen wird 256K empfohlen. Diese Option hat einen großen Einfluss auf die Leistung und erfordert strenge Tests.
-Xx: Permsize: Setzen Sie den Anfangswert der permanenten Generation (Perm Gen). Der Standardwert beträgt 1/64 des physischen Speichers.
-Xx: maxpermSize: Setzen Sie den Maximalwert der persistenten Generation. 1/4 des physischen Gedächtnisses.
2. Überlauf des Methodenbereichs
Da der Methodenbereich die relevanten Informationen der Klasse enthält, läuft der Methodenbereich, wenn wir zu viele Klassen laden, überflutet. Hier versuchen wir, den Methodenbereich durch zwei Methoden zu überfließen: JDK Dynamic Proxy und CGGLIB Proxy.
2.1 JDK Dynamischer Proxy
Paket com.cdai.jvm.overflow; Import Java.lang.reflect.InvocationHandler; import Java.lang.reflect.Method; importieren java.lang.reflect.proxy; public class methodAreeAverflow {statische Schnittstelle oomiinterface {} statische Klasse oomObject implementiert oomiInterface {} statische Klasse oomObject2 implementiert oomiInterface {} public static void main (String [] args) {endgültig oomobject = new oomobject (); while (true) { OOMIinterface proxy = (OOMIinterface) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), OOMObject.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println ("Interceptor1 funktioniert"); System.out.println (proxy.getClass ()); System.out.println ("Proxy1:" + Proxy); OomiInterface proxy2 = (oomiinterface) proxy.newproxyinstance (thread.currentThread (). GetContextClassloader (), oomobject.class.getInterfaces (), New InvocationHandler () {@override öffentliche Objekt -Objekt -Objekt -Proxy, Methode, Objekt, Objekt [] arg. funktioniert "); return methode.invoke (Objekt, args);}}); System.out.println (proxy2.getClass ()); System.out.println ("proxy2:" + proxy2); }}} Obwohl wir weiterhin die Methode proxy.newinstance () anrufen, um die Proxy -Klasse zu erstellen, hat der JVM keinen Speicherüberlauf.
Jeder Anruf generiert eine andere Instanz der Proxy -Klasse, aber das Klassenobjekt der Proxy -Klasse hat sich nicht geändert. Ist es Proxy
Hat die Klasse einen Cache für das Klassenobjekt der Proxy -Klasse? Die spezifischen Gründe werden im nachfolgenden "JDK Dynamic Proxy und Cglib" ausführlich analysiert.
2.2 CGGLIB -Agent
CGLIB wird auch das Klassenobjekt der Proxy -Klasse zwischenspeichert, aber wir können es so konfigurieren, dass das Klassenobjekt nicht zwischenstrahlt wird.
Auf diese Weise kann der Zweck des Überflusses des Methodenbereichs erreicht werden, indem wiederholt Proxy -Klassen erstellt werden.
Paket com.cdai.jvm.overflow; import Java.lang.reflect.Method; import net.sf.cglib.proxy.enhancer; import net.sf.cglib.proxy.methodinterceptor; import net.sf.cglib.proxy.methodproxy; public class methodareAverflow2 {statische Klasse oomobject {} public static void main (String [] args) {while (true) {Enhancer Enhancer = new Enhancer (); Enhancer.SetsuperClass (oomobject.class); Enhancer.SetUsecache (Falsch); Enhancer. Oomobject proxy = (oomobject) Enhancer.create (); System.out.println (proxy.getClass ()); }}}
3. Haufen Überlauf
Haufen Überlauf ist relativ einfach. Sie können den Haufen überlaufen, indem Sie ein großes Array -Objekt erstellen.
Paket com.cdai.jvm.overflow; public class heapoverflow {private statische endgültige int mb = 1024 * 1024; @Suppresswarnings ("unbenutzt") public static void main (String [] args) {byte [] BigMemory = new byte [1024 * mb]; }}
4. Stack Overflow
Der Stapelüberlauf ist ebenfalls häufig. Manchmal, wenn der rekursive Aufruf, den wir schreiben, nicht die korrekte Beendigung des Kündigungsbedingungen hat, wird die Methode weiterhin wiederholt, die Tiefe des Stapels steigt weiter und schließlich tritt der Stapelüberlauf auf.
Paket com.cdai.jvm.overflow; öffentliche Klasse Stackoverflow {private statische Int StackDepth = 1; public static void stackoverflow () {StackDepth ++; stackoverflow (); } public static void main (String [] args) {try {stackoverflow (); } catch (Ausnahme e) {System.err.println ("Stacktiefe:" + StackDepth); E. printstacktrace (); }}}