Heim > Java > javaLernprogramm > Java Virtual Machine 14: Änderungen der Java-Objektgröße, des Objektspeicherlayouts und des Sperrstatus

Java Virtual Machine 14: Änderungen der Java-Objektgröße, des Objektspeicherlayouts und des Sperrstatus

巴扎黑
Freigeben: 2017-06-26 10:39:50
Original
1124 Leute haben es durchsucht

Wie viele Bytes belegt ein Objekt?

In Bezug auf die Größe des Objekts gibt es für C/C++ eine sizeof-Funktion, die direkt abgerufen werden kann, aber Java scheint über eine solche Methode nicht zu verfügen. Glücklicherweise wurde die Instrumentation-Klasse nach JDK 1.5 eingeführt. Diese Klasse bietet eine Methode zur Berechnung des Speicherbedarfs eines Objekts. Auf die Verwendung der spezifischen Instrumentierungsklasse werde ich nicht näher eingehen. Informationen zum genauen Messen der Größe von Java-Objekten finden Sie in diesem Artikel.

Aber ein Unterschied besteht darin, dass dieser Artikel die Befehlszeile verwendet, um JVM-Parameter zur Angabe des Agenten zu übergeben. Hier stelle ich die JVM-Parameter über Eclipse ein:

Was folgt, ist der spezifische Pfad zur Datei agent.jar, die ich eingegeben habe. Über den Rest werde ich nicht sprechen, aber werfen Sie einen Blick auf den Testcode:

 1 public class JVMSizeofTest { 2  3     @Test 4     public void testSize() { 5         System.out.println("Object对象的大小:" + JVMSizeof.sizeOf(new Object()) + "字节"); 6         System.out.println("字符a的大小:" + JVMSizeof.sizeOf('a') + "字节"); 7         System.out.println("整型1的大小:" + JVMSizeof.sizeOf(new Integer(1)) + "字节"); 8         System.out.println("字符串aaaaa的大小:" + JVMSizeof.sizeOf(new String("aaaaa")) + "字节"); 9         System.out.println("char型数组(长度为1)的大小:" + JVMSizeof.sizeOf(new char[1]) + "字节");10     }11     12 }
Nach dem Login kopieren

Das laufende Ergebnis ist:

Object对象的大小:16字节
字符a的大小:16字节
整型1的大小:16字节
字符串aaaaa的大小:24字节
char型数组(长度为1)的大小:24字节
Nach dem Login kopieren

Dann bleibt der Code unverändert, fügen Sie einen virtuellen Maschinenparameter „-XX:-UseCompressedOops“ hinzu und führen Sie die Testklasse erneut aus. Das laufende Ergebnis lautet:

Object对象的大小:16字节
字符a的大小:24字节
整型1的大小:24字节
字符串aaaaa的大小:32字节
char型数组(长度为1)的大小:32字节
Nach dem Login kopieren

Der Grund wird später im Detail erläutert.

Java-Objektgrößenberechnungsmethode

JVM-Größe für gewöhnliche Objekte und Array-Objekte Die Berechnungsmethode ist anders, ich habe zur Veranschaulichung ein Bild gezeichnet:

Erklären Sie jeden Teil:

  1. Mark Word: Zeichnet Informationen auf, wenn das Objekt gespeichert wird, und die belegte Speichergröße entspricht der Anzahl der Maschinenziffern, d. h. 32-Bit-Maschine belegt 4 Bytes, 64-Bit-Maschine belegt 8 Bytes

  2. Metadatenzeiger: Zeigt auf das Klass-Objekt, das den Typ beschreibt (das C++-Gegenstück). des Java-Klassenzeigers enthält das Klass-Objekt Metadaten des Typs, zu dem das Instanzobjekt gehört. Daher wird dieses Feld als Metadatenzeiger bezeichnet. Die JVM verwendet diesen Zeiger häufig, um die Typinformationen im Methodenbereich zur Laufzeit zu lokalisieren . Die Größe dieser Daten wird später besprochen

  3. Array-Länge: eindeutig für Array-Objekte, ein Referenztyp, der auf den Typ int zeigt und zur Beschreibung der Länge des verwendet wird Array, die Größe dieser Daten. Die gleiche Größe wie der Metadatenzeiger, später auch

  4. Instanzdaten: Instanzdaten sind Die 8 Basisdatentypen sind Byte, Short, Int, Long, Float, Double, Char und Boolean (Objekttypen bestehen auch aus diesen 8 Basisdatentypen. Wie viele Bytes belegt jeder Datentyp?). wird nicht einzeln aufgelistet.

  5. Padding: Variable, Die Ausrichtung von HotSpot ist eine 8-Byte-Ausrichtung, das heißt, ein Objekt muss vorhanden sein ein ganzzahliges Vielfaches von 8 Bytes . Wenn also die letzte Datengröße 17 beträgt, füllen Sie sie mit 7, und wenn die vorherige Datengröße 18 beträgt, füllen Sie sie mit 6 usw.

Lassen Sie uns abschließend über die Größe des Metadatenzeigers sprechen. Der Metadatenzeiger ist ein Referenztyp, daher sollte der 64-Bit-Maschinenmetadatenzeiger normalerweise 8 Byte groß sein und der 32-Bit-Maschinenmetadatenzeiger sollte 4 Byte groß sein. In HotSpot gibt es jedoch eine Optimierung, die den Metadatentypspeicher komprimiert , verwenden Sie JVM-Parameter:

  • -XX:+UseCompressedOops, um die Komprimierung zu aktivieren

  • - XX:-UseCompressedOops deaktiviert die Komprimierung

HotSpot verwendet standardmäßig Ersteres, was bedeutet, dass die Metadatenzeigerkomprimierung aktiviert ist Eine 64-Bit-Maschine belegt eine Größe von 4 Bytes. Mit anderen Worten: Wenn die Komprimierung aktiviert ist, belegt die Referenz auf dem 64-Bit-Computer 4 Bytes, andernfalls sind es die normalen 8 Bytes .

Berechnung der Java-Objektspeichergröße

Mit der oben genannten theoretischen Grundlage können wir Ihnen helfen kann die Ausführungsergebnisse der JVMSizeofTest-Klasse analysieren und herausfinden, warum die Größe desselben Objekts nach dem Hinzufügen des Parameters „-XX:-UseCompressedOops“ unterschiedlich ist.

Das erste ist die Größe des Objekts:

  1. Wenn die Zeigerkomprimierung aktiviert ist, markieren 8 Bytes das Wort + 4 Wörter Abschnittsmetadatenzeiger = 12 Bytes, da 12 Bytes kein Vielfaches von 8 sind, werden also 4 Bytes gefüllt und das Objektobjekt belegt 16 Bytes Speicher

  2. Schließen Wenn Zeiger komprimiert sind, sind 8 Byte Markierungswort + 8 Byte Metadatenzeiger = 16 Byte. Da 16 Byte genau ein Vielfaches von 8 sind, müssen keine Bytes gefüllt werden. Das Objekt belegt 16 Byte Speicher

Dann ist die Größe des Zeichens „a“:

  1. Wenn die Zeigerkomprimierung erfolgt eingeschaltet, 8-Byte-Markierungswort + 4 Bytes Metadatenzeiger + 1 Byte-Zeichen = 13 Bytes, da 13 Bytes kein Vielfaches von 8 sind, also 3 Bytes aufgefüllt werden, belegt das Zeichen „a“ 16 Bytes Speicher

  2. Wenn die Zeigerkomprimierung deaktiviert ist, sind 8 Bytes Markierungswort + 8 Bytes Metadatenzeiger + 1 Byte Zeichen = 17 Bytes, da 17 Bytes kein Vielfaches von 8 sind, werden sie mit 7 aufgefüllt Bytes, das Zeichen 'a' belegt 24 Bytes Speicher

Dann ist die Größe der Ganzzahl 1:

  1. Wenn die Zeigerkomprimierung aktiviert ist, sind 8 Byte Mark Word + 4 Byte Metadatenzeiger + 4 Byte Int = 16 Byte. Da 16 Byte genau ein Vielfaches von 8 sind, müssen keine Bytes gefüllt werden , Ganzzahltyp 1 belegt 16 Byte Speicher

  2. Wenn die Zeigerkomprimierung deaktiviert ist, werden 8-Byte-Markierungswort + 8-Byte-Metadatenzeiger + 4-Byte-Int = 20 Bytes. Da 20 Bytes genau ein Vielfaches von 8 sind, wird es mit 4 Bytes aufgefüllt. Die Ganzzahl 1 belegt 24 Bytes Speicher

gefolgt von string Die Größe von „aaaaa“, alle statischen Felder müssen nicht kontrolliert werden, nur die Instanzfelder konzentrieren sich auf die Instanzfelder im String-Objekt, die „char value[]“ und „int hash“ enthalten daraus:

  1. Wenn die Zeigerkomprimierung aktiviert ist, 8-Byte-Markierungswort + 4-Byte-Metadatenzeiger + 4-Byte-Referenz + 4-Byte-int = 20 Bytes. Da 20 Bytes kein Vielfaches von 8 sind, also 4 Bytes auffüllen, belegt die Zeichenfolge „aaaaa“ 24 Bytes Speicher

  2. Wenn die Zeigerkomprimierung erfolgt ausgeschaltet, 8-Byte-Markierungswort + 8-Byte-Metadatenzeiger + 8-Byte-Referenz + 4 Bytes int = 28 Bytes, da 28 Bytes kein Vielfaches von 8 sind, also 4 Bytes aufgefüllt werden, belegt die Zeichenfolge „aaaaa“ 32 Bytes des Speichers

Das letzte ist die Größe des char-Arrays mit der Länge 1:

  1. Wenn die Zeigerkomprimierung aktiviert ist, 8 Byte Markierungswort + 4 Byte Metadatenzeiger + 4 Byte Array-Größenreferenz + 1 Byte Zeichen = 17 Byte. Da 17 Byte kein Vielfaches von 8 sind, werden 7 Byte aufgefüllt. Ein char-Array der Länge 1 belegt 24 Wörter Abschnittsspeicher

  2. Wenn die Zeigerkomprimierung deaktiviert ist, 8 Bytes Mark Word + 8 Bytes Metadatenzeiger + 8 Bytes der Array-Größenreferenz + 1 Wort Abschnitt char = 25 Bytes Da 25 Bytes kein Vielfaches von 8 sind, wird es mit 7 Bytes gefüllt. Das char-Array mit der Länge 1 belegt 32 Bytes Speicher

Mark Word

Mark Word wurde schon einmal gesehen, es ist ein sehr wichtiger Teil davon der Java-Objektheader. Mark Word speichert die laufenden Daten des Objekts selbst, wie Hash-Code (HashCode), Alter der GC-Generierung, Identifizierung des Sperrstatus, von Threads gehaltene Sperren, voreingenommene Thread-ID, voreingenommener Zeitstempel usw.

Da das Objekt jedoch viele Laufzeitdaten speichern muss, überschreitet es tatsächlich die Grenze, die die 32-Bit- und 64-Bit-Bitmap-Strukturen aufzeichnen können sind Daten, die mit dem Objekt selbst definiert werden, und zwar unter Berücksichtigung der Platzeffizienz der virtuellen Maschine. Mark Word ist als nicht feste Datenstruktur konzipiert Speichern Sie möglichst viele Informationen auf kleinstem Raum. Wenn beispielsweise das Objekt in der virtuellen 32-Bit-HotSpot-Maschine nicht gesperrt ist, werden 25 Bits im 32-Bit-Bereich von Mark Word zum Speichern des Objekt-Hashcodes (HashCode) und 4 Bits zum Speichern des Objektgenerierungsalters verwendet , und 2 Bits werden zum Speichern des Objektgenerierungsalters verwendet. Speichersperre-Identifikationsbit, 1Bit festes Bit 0. Der Speicherinhalt von Objekten in anderen Zuständen (leichte Sperre, schwere Sperre, GC-Markierung, voreinstellbar) ist wie folgt:

Was hier besondere Aufmerksamkeit erfordert, ist der Sperrstatus. Der Sperrstatus und Änderungen im Sperrstatus werden später untersucht.

Upgrade des Schlosses

Wie im Bild oben gezeigt, gibt es vier Schlösser Staaten: Kein Sperrstatus, voreingenommene Sperren, leichte Sperren und schwere Sperren wurden ab JDK1.6 eingeführt, um den Leistungsverbrauch durch den Erwerb und die Freigabe von Sperren zu reduzieren.

Der Status der vier Schlösser wird mit der Konkurrenz allmählich eskalieren. Schlösser können aufgerüstet, aber nicht herabgestuft werden, was bedeutet, dass voreingenommene Schlösser zu leichten Schlössern aufgerüstet werden können, leichte Schlösser jedoch nicht herabgestuft werden können Der Zweck der Voreingenommenheit der Sperre besteht darin, die Effizienz beim Erfassen und Freigeben von Sperren zu verbessern. Verwenden Sie ein Diagramm, um diese Beziehung darzustellen:

Bias-Sperre

HotSpot-Autor bestanden Frühere Untersuchungen haben ergeben, dass es in den meisten Fällen nicht nur keinen Multi-Thread-Wettbewerb um Sperren gibt, sondern auch, dass immer mehrere Threads denselben Thread erwerben. Um den Code für Threads zum Erwerb von Sperren zu verringern, werden voreingenommene Sperren eingeführt. Der Prozess zum Erhalten der Bias-Sperre ist:

  1. Greifen Sie auf Mark Word zu, um zu sehen, ob das Bias-Lock-Flag auf 1 gesetzt ist und ob das Flag-Bit 01 ist-- --Bestätigen Sie, dass es sich um einen Biasable-Zustand handelt.

  2. Wenn es sich um einen Biasable-Zustand handelt, testen Sie, ob die Thread-ID auf den aktuellen Thread verweist. Wenn ja, führen Sie (5) aus. , andernfalls führen Sie (3) aus

  3. Wenn die Thread-ID nicht auf den aktuellen Thread verweist, konkurrieren Sie um die Sperre durch den CAS-Vorgang. Wenn der Wettbewerb erfolgreich ist, setzen Sie die Thread-ID in Mark Word auf die aktuelle Thread-ID und führen Sie dann (5) aus. Wenn der Wettbewerb fehlschlägt, führen Sie (4)

  4. Wenn es CAS nicht gelingt, die voreingenommene Sperre zu erhalten, bedeutet das, dass es Konkurrenz gibt. Wenn der globale Sicherheitspunkt (Safepoint) erreicht ist, wird der Thread, der die voreingenommene Sperre erhalten hat, angehalten und die voreingenommene Sperre wird zu einer leichten Sperre aktualisiert (da die voreingenommene Sperre keine Konkurrenz voraussetzt, aber hier Wettbewerb herrscht und die voreingenommene Sperre benötigt wird). aktualisiert werden soll), und dann führt der am sicheren Punkt blockierte Thread weiterhin den Synchronisationscode aus

  5. Führt den Synchronisationscode aus

Sobald es erworben ist, wird es freigegeben. Der Freigabepunkt der voreingenommenen Sperre liegt in Schritt (4) oben Nur ​​wenn andere Threads versuchen, darum zu konkurrieren Bei der vorgespannten Sperre gibt der Thread, der die vorgespannte Sperre hält, die Sperre frei, der Thread gibt die vorgespannte Sperre nicht aktiv frei. Der Freigabeprozess der voreingenommenen Sperre ist:

  1. Es muss auf den globalen Sicherheitspunkt gewartet werden (zu diesem Zeitpunkt wird kein Bytecode ausgeführt)

  2. Zuerst wird der Thread angehalten, der die Bias-Sperre besitzt, und festgestellt, ob das Sperrobjekt gesperrt ist

  3. Die Bias-Sperre wird freigegeben. Dann wird der entsperrte Zustand (Identifikationsbit ist 01) oder die leichte Sperre (Identifikationsbit ist 00) wiederhergestellt

Lightweight-Schloss

Der Schließvorgang des Lightweight-Schloss ist:

  1. Geben Sie den Code ein Wenn beim Synchronisieren eines Blocks der Sperrstatus des Synchronisierungsobjekts sperrenfrei ist, erstellt die JVM zunächst einen Bereich namens Sperrdatensatz im Stapelrahmen des aktuellen Threads, um eine Kopie des aktuellen Markierungsworts des Sperrobjekts zu speichern. Offiziell als „Displaced Mark Word“ bezeichnet, ist der Status des Thread-Stapels und des Objekt-Headers zu diesem Zeitpunkt wie in der Abbildung dargestellt Objekt-Header zum Sperrdatensatz

  2. Nach erfolgreichem Kopieren versucht die JVM mithilfe der CAS-Operation, das Markierungswort des Objekts auf einen Zeiger zu aktualisieren zum Sperrdatensatz und speichern Sie ihn im Sperrdatensatz. Der Besitzerzeiger zeigt auf das Objektmarkierungswort. Wenn die Aktualisierung erfolgreich ist, führen Sie Schritt (4) aus, andernfalls führen Sie Schritt (5) aus

    Wenn die Aktualisierungsaktion erfolgreich ist, dann ist der aktuelle Thread Eigentümer der Sperre des Objekts, und das Sperrflag des Objekts Mark Word wird auf 00 gesetzt, was bedeutet, dass sich das Objekt in einem befindet Lightweight-Sperrstatus. Zu diesem Zeitpunkt ist der Status des Thread-Stacks und des Objekt-Headers wie in der Abbildung dargestellt

  3. Wenn die Aktualisierungsaktion fehlschlägt, Die JVM prüft zunächst, ob das Markierungswort des Objekts auf den Stapelrahmen des aktuellen Threads verweist. Wenn dies der Fall ist, bedeutet dies, dass der aktuelle Thread bereits die Sperre dieses Objekts besitzt. Anschließend können Sie den synchronisierten Block direkt eingeben Ausführung. Andernfalls bedeutet dies, dass mehrere Threads um die Sperre konkurrieren und die leichte Sperre zu einer schweren Sperre erweitert wird und der Statuswert der Sperrkennung 10 wird. In Mark Word wird der Zeiger auf die schwere Sperre gespeichert Die auf die Sperre wartenden Threads treten später ebenfalls in den Blockierungsstatus ein. Der aktuelle Thread versucht, Spin zu verwenden, um die Sperre zu erhalten, und verwendet eine Schleife, um die Sperre zu erhalten
  4. Vergleich von vorgespannten Schlössern, leichten Schlössern und schweren Schlössern

    Im Folgenden wird eine Tabelle zum Vergleich von vorgespannten Schlössern und leichten Schlössern verwendet. Klassenschlösser und schwere Schlösser Schlösser, ich habe sie online gesehen und fand sie sehr gut geschrieben, um mein Gedächtnis zu vertiefen, habe ich sie noch einmal von Hand getippt:

Das obige ist der detaillierte Inhalt vonJava Virtual Machine 14: Änderungen der Java-Objektgröße, des Objektspeicherlayouts und des Sperrstatus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage