JVmmemoryModel
In diesem Artikel wird hauptsächlich den in der JVM -Spezifikation beschriebenen Laufzeitbereich (runtimedataaras) vorgestellt. Diese Bereiche sind so konzipiert, dass Daten, die von der JVM selbst verwendet werden, oder Programme, die auf dem JVM ausgeführt werden.
Lassen Sie uns zunächst den JVM über einen Überblick über die Bytecode einführen und schließlich verschiedene Datenbereiche einführen.
Überblick
Als Abstraktion des Betriebssystems stellt JVM sicher, dass derselbe Code auf verschiedenen Hardware- oder Betriebssystemen konsequent verhält.
Zum Beispiel:
Für den Basistyp int handelt es sich um eine 32-Bit-Signiertelbezahlung, unabhängig vom 16-Bit/32-Bit/64-Bit-Betriebssystem. Reichen von -2^31 bis 2^31-1
Unabhängig davon, ob das Betriebssystem oder die Hardware eine große oder kleine Byte-Reihenfolge ist, wird sichergestellt, dass die von der JVM gespeicherten und verwendeten Daten in großer oder kleiner Byte-Reihenfolge (Lesen Sie das Byte mit hohem Bit zuerst).
Verschiedene JVM -Implementierungen können sich etwas unterscheiden, sind jedoch im Allgemeinen gleich.
Das obige Bild ist ein Überblick über einen JVM
Der JVM interpretiert den Compiler-generierten Bytecode. Obwohl JVM die Abkürzung von Java -Virtual -Maschinen ist, kann sie eine Sprache, die in Bytecode zusammengestellt werden kann, auf der Grundlage der JVM wie Scala, Groovy <�/kf/Ware/VC/"kompiliert werden kann. target = "_ leer"> vcd4ncjxwps6qwcux3mpixrw3sbxetmxfzekvt6OS19A92SlRU+GXU2NSYXNZBG9HZGVYVNPU2LKIU7Q05R W91MVQ0MQXYV2+3CF41TC1XNK7UPBH+NPYO6ZWQRXAVNPU2MV8TCRJBGFZC2XVYWRLCRG7Z/Q72BVY1D9KVK3NO9A51MVQ0KGJPC 9WPG0KPHA+VNPU2LXE19A92SLRZAI5/DA00NDS/CFMKGV4ZWN1DGLVBIBLBMDPBMUPVFJQ0L3IYS26ZDA00NA8L3A+DQO8CD7WT NDQ0V3H5TDO0QQ05RSIS8ZQ8SNPZ8LOXKOSSCJI57PM0PLWTNDQTB3EXNK70NCJRLVY1D/K/B7DVMBL47XE1TC85L3HUFS8L3A+D QO8CD7WTNDQ0V3H5TKYULRU8LSMWO3T67XSUOY2DF3Z7XNS7XEVBU7PTWVCD4NCJXWPIOQUTY24EPWTBA8YRXP1SHLVLTKSBHG 0UU5PSTCKKEPJVD1QDXN0IGLUIHRPBWWUPOANKSVS+ZCRHSNG+RBOJ1RTQ0LXETPRC6YJiYLJTPRC6YMX4NLRS8MXVRXYTPRC6YHO Yxrpdmugq29kzsmho7TMT8VKSVSX4NLRYFQZYBT6WUU1XMF40/KZXS6QPC9WPG0KPHA+TPRC67U6TOBH+CHDB2RLIENH Y2gpoao8tmqxseds67y8yvuosklukby2tpo1xmzhun/by0pwtbxe0nte3Coqpc9WPG0KPGGYIGLKPQ == "Stack-basiert Architektur "> Stack-basierte Architektur
JVM verwendet eine stackbasierte Architektur. Obwohl der Stapel für Entwickler transparent ist, spielt er eine sehr wichtige Rolle oder einen Einfluss auf den erzeugten Bytecode und JVM.
Die von uns entwickelten Programme werden Tiefst- und Leistungsbetriebsbetrieb umwandeln und in Bytecode speichern. Karte zu Betriebsanweisungen durch Operanden im JVM. Gemäß der JVM -Spezifikation werden die von den Betriebsanweisungen erforderlichen Parameter aus dem Operand -Stapel erhalten.
Lassen Sie uns ein Beispiel für das Hinzufügen von zwei Zahlen geben. Diese Operation heißt IADD. Das Folgende ist der Prozess von 3+4 in Bytecode
Erster Schieben 3 und 4 in den Operand -Stapel
Rufen Sie die IADD -Richtlinie an
Die IADD -Anweisung taucht 2 Nummern von der Spitze des Operand -Stapels auf
Das Ergebnis von 3+4 wird für die spätere Verwendung in den Operand -Stapel geschoben
Dieser Ansatz wird als stackbasierte Architektur bezeichnet. Es gibt noch andere Möglichkeiten, um mit niedrigem Level-Operationen wie registrierter Architektur zu handhaben.
Bytecode
Java-Bytecode ist das Ergebnis des Konvertierens von Java-Quellcode in eine Reihe von Operationen auf niedriger Ebene. Jeder Vorgang besteht aus einem Opcode- oder Betriebscode mit Null- oder mehr Byte -Länge -Parametern (die meisten Operationen verwenden jedoch Parameter, die über den Operand -Stapel erhalten werden). Ein Byte kann 256 Zahlen von 0x00 bis 0xff darstellen und derzeit bis Java8 werden insgesamt 204 verwendet.
Die folgenden Listen sind verschiedene Arten von Bytecode -Opcodes sowie deren Reichweite und einfache Beschreibung auf
Konstanten: Schieben Sie den Wert des konstanten Pools oder eines bekannten Wertes in den Operand -Stapel. 0x00 - 0x14
Lasten: Drücken Sie lokale Variablenwerte in den Operand -Stapel. 0x15 - 0x35
Speicher: Ladenwert von Operand Stack zur lokalen Variablen 0x36 - 0x56
Stack: Process Operand Stack 0x57 - 0x5f
Mathematik: Wert aus Operand Stack für die grundlegende mathematische Berechnung 0x60 - 0x84
Konvertierungen: Konvertieren Sie zwischen den Typen 0x85 - 0x 93
COMAPRISONS: Vergleichsbetrieb von zwei Werten 0x94 - 0xa6
Steuerelemente: Goto, Rückkehr, Schleife usw. Führen Sie Steuervorgänge 0xa7 - 0xb1 aus
Referenzen: Führen Allokation Objekte oder Arrays aus und erhalten oder überprüft Verweise auf Objekte, Methoden und statische Methoden. Statische Methoden können auch aufgerufen werden. 0xb2 - oxc3
Erweitert: Erweitert: Operationen aus den anderen Kategorien, die nach dem Hinzufügen von nachgegeben wurden. Von Wert 0xc4 bis 0xc9
(Was bedeutet dieser Satz?
Reserviert: Die JVM -Implementierung verwendet Slots 0xca, Oxfe, Oxff
Diese 204 Operationen sind sehr einfach, geben Sie einige Beispiele an
IFEQ (0x99) bestimmt, ob die beiden Werte gleich sind
IADD (0x60) fügt zwei Zahlen hinzu
i2l (0x85) konvertiert ein bisschen lang
ArrayLength (0xbe) gibt die Arraylänge zurück
Pop (0x57) Pop einen Wert von der Oberseite des Operand -Stapels
Wir brauchen einen Compiler, um Bytecode -Dateien zu erstellen, und der Standard -Java -Compiler ist Javac in JDK.
public class test {public static void main (String [] args) {int a = 1; int b = 15; int result = add (a, b); } public static int add (int a, int b) {int result = a + b; Rückgabeergebnis; }}Sie können die Bytecode -Datei "test.class" über "Javac test.java" erhalten. Bytecode -Dateien sind binär. Wir können binäre Bytecode -Dateien über JAVAP in Textform umwandeln
Java -Verbose -Test.CLASS
ClassDile /c:/tmp/test.class zuletzt modifiziert 1 avr. 2015; Größe 367 Bytes MD5 Checksum ADB9FF75F12FC6CE1CDDE22A9C4C7426 COSCEDD aus "test.java" öffentliche Klasse com.Codinggeek.jvm.Test SourceFile: "Test.java" -Minorversion: 0 Hauptversion: 51 Flags: ACCREF accref #acc. acc. acc. acc. acc. acc. acc. acc. java/lang/Object."<init>":()V #2 = Methodref #3.#16 // com/codinggeek/jvm/Test.add:(II)I #3 = Class #17 // com/codinggeek/jvm/Test #4 = Class #18 // java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 Leinenumbertable #9 = UTF8 Main #10 = UTF8 ([ljava/lang/string;) v #11 = utf8 add #12 = utf8 (ii) i #13 = utf8 SourceFile #14 = utf8 test.java #15 = namandtype #5 // "<init>" :() v #16 = namandtype #: #"<init>". #17 = utf8 com/codinggeek/jvm/test #18 = utf8 java/lang/objekt {public com.codinggeek.jvm.test (); Flags: ACC_PUBLIC CODE: STACK = 1, LOCALS = 1, Args_size = 1 0: ALOAD_0 1: Invokespecial #1 // Methode Java/Lang/Objekt. flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: iconst_1 1: istore_1 2: bipush 15 4: istore_2 5: iload_1 6: iload_2 7: invokestatic #2 // Method add:(II)I 10: istore_3 11: return LineNumberTable: line 6: 0 line 7: 2 line 8: 5 Zeile 9: 11 öffentliches statisches int add (int, int); Flags: ACC_PUBLIC, ACC_STATISCHE CODE: STACK = 2, LOCALS = 3, Args_Size = 2 0: ILOAD_0 1: ILOAD_1 2: IADD 3: ISTORE_2 4: ILOAD_2 5: Ireturn Linenumbertable: Zeile 12: 0 Zeile 13: 4}Es ist zu sehen, dass Bytecode nicht nur eine einfache Übersetzung von Java -Code ist, sondern enthält:
Eine konstante Poolbeschreibung der Klasse. Konstante Pools sind JVM -Datenbereiche, die zum Speichern von Klassenmetadaten verwendet werden, z. B. Methodamen, Parameterlisten usw. innerhalb der Klasse. Wenn das JVM eine Klasse lädt, wird die Metadaten in den konstanten Pool geladen
Geben Sie die spezifischen Positionsinformationen von Funktionen und Tmall -Variablen in Bytecode über die Zeilennummer -Tabelle und die lokale Variable -Tabelle an.
Übersetzung von Java -Code (einschließlich versteckter Elternklassenkonstrukte)
Bietet spezifischere Vorgänge für Operand -Stapel und umfassendere Möglichkeiten, Parameter zu übergeben und zu erhalten
Unten finden Sie eine einfache Beschreibung der Bytecode -Dateispeicherinformationen
ClassDile {u4 Magic; u2 minor_version; U2 major_version; u2 constant_pool_count; cp_info constant_pool [constant_pool_count-1]; u2 access_flags; u2 this_class; U2 Super_Class; u2 interfaces_count; u2 interfaces [interfaces_count]; u2 fields_count; field_info fields [fields_count]; U2 Attribute_Count; Attribute_info Attribute [Attribute_Count];}Laufzeitdatenbereich
Der Laufzeitbereich ist der Speicherbereich, der zum Speichern von Daten entwickelt wurde. Diese Daten sind für Entwickler oder JVM intern verwendet.
Haufen
Der Haufen wird erstellt, wenn der JVM gestartet wird und von allen JVM -Threads geteilt wird. Alle Klasseninstanzen und Arrays werden dem Haufen (erstellt von neu) zugeteilt.
Der Haufen muss von einem Müllsammler verwaltet werden, der für die Freigabe von Objekten verantwortlich ist, die vom Entwickler erstellt wurden und nicht erneut verwendet werden.
In Bezug auf die Strategie zur Müllsammlung wird sie durch die JVM -Implementierung bestimmt (z. B. bietet Hotspot mehrere Algorithmen).
Es gibt eine maximale Grenze für den Heap -Speicher. Wenn dieser Wert überschritten wird, wird der JVM eine Ausnahme aus der OutofMemroy auswerfen.
Methodenbereich
Der Methodenbereich wird auch von allen Threads des JVM geteilt. Gleiches gilt für JVM -Startup. Die im Methodenbereich gespeicherten Daten werden vom Bytecode vom Klassenloader geladen, der während des Anwendungslaufs konsistent vorhanden ist, sofern der Klassenlader nicht zerstört oder der JVM gestoppt wird.
Der Methodenbereich speichert die folgenden Daten:
Klasseninformationen (Attributname, Methodenname, übergeordnete Klassenname, Entschuldigung Name, Version usw.)
Methoden und konstruierte Bytecodes
Der konstante Laufzeitpool erstellt beim Laden jeder Klasse
Die JVM -Spezifikation erzwingt nicht die im Haufen implementierten Methodenbereiche. Vor JAVA7 implementierte Hotspot Methodenzonen unter Verwendung einer Region namens Permgen. Das permanente Band liegt neben dem Heap (die Speicherverwaltung ist mit dem Heap dieselbe), das Standardbit beträgt 64 MB
Ausgehend von Java8 verwendet HPTSpot einen separaten lokalen Speicher, um den Methodenbereich zu implementieren und den Metadatenbereich (MetaPace) zu benennen. Der maximal verfügbare Platz im Metadatenbereich ist der verfügbare Speicher des gesamten Systems.
Wenn die Methode nicht für den verfügbaren Speicher angewendet werden kann, wird der JVM auch das OutofMemoryError herausgeworfen.
Laufzeit konstanter Pool
Der konstante Laufzeitpool ist Teil des Methodenbereichs. Aufgrund der Bedeutung des Ausführens eines konstanten Pools zu Metadaten wird er in der Java -Spezifikation außerhalb des Methodenbereichs getrennt beschrieben. Der konstante Laufzeitpool wächst mit den geladenen Klassen und Schnittstellen.
Konstante Pools sind in herkömmlichen Sprachen eine Syntaxtabelle. Mit anderen Worten, wenn eine Klasse, Methode oder Eigenschaft aufgerufen wird, sucht die JVM nach der realen Adresse dieser Daten im Speicher über den konstanten Laufzeit -Pool. Der konstante Laufzeitpool enthält auch Konstanten von Streichliteralen oder primitiven Typen
Sirng mystring = "Dies ist eine Zeichenfolge, die litteral ist" statische endgültige int my_constant = 2;
PC (Programmzähler) Register (pro Thread) Das PC -Register (pro Thread)
Jeder Thread hat ein eigenes PC -Register (Programmzähler), das zusammen mit der Erstellung von Thread erstellt wird. Jeder Thread kann nur eine Methode zu einem Zeitpunkt ausführen, der als aktuelle Methode des Threads bezeichnet wird. Das PC -Register enthält die Adresse des JVM, das derzeit den Anweisungen ausführt (im Methodenbereich).
Wenn die aktuell ausgeführte Methode eine lokale Methode ist, ist der Wert des PC -Registers undefiniert
Virtual Machine Stack pro Thread-Java-Virtual-Maschinen-Stapel pro Thread "> Virtual Machine Stack (pro Thread) Java Virtual Machine Stacks (pro Thread)
Der virtuelle Maschinenstapel speichert mehrere Frames. Bevor Sie den Stapel beschreiben, schauen wir uns zuerst die Frames an.
Rahmen
Ein Bild ist eine Datenstruktur, die mehrere Daten enthält, die den aktuellen Methodenzustand darstellen, den der Thread ausführt:
Operand Stack: Wie bereits erwähnt, verwenden Bytecode -Anweisungen Operand Stack, um Parameter zu übergeben
Lokales Variablenarray: Dieses Array enthält alle lokalen Variablen in einem Umfang der aktuell ausgeführten Methode. Dieses Array kann einen primitiven Typ, eine Referenz oder eine Absenderadresse enthalten. Die Größe des lokalen variablen Arrays wird zum Kompilierungszeitpunkt bestimmt. JVM verwendet lokale Variablen, um beim Aufrufen der Methode Parameter zu übergeben, und das lokale variable Array der aufgerufenen Methode wird über den Operand -Stapel der aufrufenden Methode erstellt.
Laufzeitkonstante Poolreferenz: Verweise auf den konstanten Pool der aktuellen Methode der aktuellen Klasse. Der JVM verwendet konstante Poolreferenzen, um Signale für reale Speicherreferenzen zu übergeben.
Stack (Stack)
Jeder JVM -Thread verfügt über einen privaten JVM -Stack, der gleichzeitig mit dem Thread erstellt wird. Java Virtual Machine Stapel speichert Frames. Jedes Mal, wenn eine Methode aufgerufen wird, wird ein Rahmen erstellt und in den virtuellen Maschinenstapel gedrückt. Wenn diese Methode ausgeführt wird, wird auch der Rahmen zerstört (unabhängig davon, ob die Methode normal ausgeführt wird oder eine Ausnahme ausgelöst wird)
Während der Ausführung eines Threads ist nur ein Frame verfügbar. Dieser Frame wird als aktueller Frame bezeichnet.
Operationen zu lokalen Variablen und Operand -Stapeln werden normalerweise von Verweisen auf den aktuellen Rahmen begleitet.
Schauen wir uns ein weiteres Beispiel für die Addition an
public int add (int a, int b) {return a + b;} public void functiona () {// ein Code ohne Funktion Aufruf int result = add (2,3); // Aufrufen zur Funktion b // Code ohne Funktionsaufruf}In der Methode A befindet sich Rahmen A der aktuelle Rahmen, der sich oben am virtuellen Maschinenstapel befindet. Zu Beginn des Aufrufens der Add -Methode wird ein neuer Rahmen B erstellt und in den virtuellen Maschinenstapel gedrückt. Frame B wird zum neuen Stromrahmen.
Das lokale variable Array von Frame B wird mit Daten im Operand-Stapel von Frame A gefüllt. Wenn die add-Methode endet, wird Rahmen B zerstört und Rahmen A als aktueller Rahmen wiederhergestellt. Das Ergebnis der Add -Methode wird in den Operand -Stapel von Frame A gedrückt, so dass Methode A das Ergebnis von Add durch den Operand -Stapel von Frame A. erhalten kann.
Zusammenfassen
In der oben genannten Daten geht es um die Datenbereichsanalyse der Java Virtual Machine -Laufzeit. Ich hoffe, es wird für alle hilfreich sein.
Wenn es Mängel gibt, hinterlassen Sie bitte eine Nachricht, um darauf hinzuweisen.