Das Klassendateiformat verwendet eine Pseudostruktur ähnlich der C-Sprachstruktur zum Speichern von Daten. In dieser Pseudostruktur gibt es nur zwei Datentypen: „vorzeichenlose Zahl“ und „Tabelle“.
·Zahlen ohne Vorzeichen sind grundlegende Datentypen. U1, u2, u4 und u8 repräsentieren vorzeichenlose Zahlen von 1 Byte, 2 Byte, 4 Byte bzw. 8 Byte. Kann zur Beschreibung von Zahlen, Indexreferenzen und quantitativen Werten verwendet werden , oder in UTF-8 codierte Zeichenfolgenwerte.
·Eine Tabelle ist ein zusammengesetzter Datentyp, der aus mehreren vorzeichenlosen Zahlen oder anderen Tabellen als Datenelementen besteht. Um die Unterscheidung zu erleichtern, enden alle Tabellennamen üblicherweise mit „_info“. Tabellen werden zur Beschreibung hierarchischer zusammengesetzter Strukturdaten verwendet. Die gesamte Klassendatei kann im Wesentlichen als Tabelle betrachtet werden. Die ersten 4 Bytes jeder Klassendatei werden als Magic Number bezeichnet. Ihre einzige Funktion besteht darin, zu bestimmen, ob diese Datei vorhanden ist ist eine Klassendatei, die von der virtuellen Maschine akzeptiert werden kann. Nicht nur Klassendateien, sondern auch viele Dateiformatstandards haben die Angewohnheit, magische Zahlen zur Identifizierung zu verwenden. Beispielsweise haben Bildformate wie GIF oder JPEG magische Zahlen im Dateikopf.
Die magische Zahl der Klassendatei ist sehr „romantisch“ und der Wert ist 0xCAFEBABE (Kaffeebaby?)
Die nächsten 4 Bytes der magischen Zahl speichern die Versionsnummer der Klassendatei: das 5. und 6. Zeichen Abschnitt ist die Nebenversionsnummer (MinorVersion), und das 7. und 8. Byte sind die Hauptversionsnummer (Major Version). Die Versionsnummer von Java beginnt bei 45. Die Hauptversionsnummer jeder nach JDK 1.1 veröffentlichten JDK-Hauptversion wird um 1 erhöht (JDK 1.0–1.1 verwendet die Versionsnummern 45.0–45.3). . Version der Klassendatei, aber Sie können keine späteren Versionen der Klassendatei ausführen In der Klassendatei sind die Daten, die am meisten mit anderen Elementen in der Klassendateistruktur verknüpft sind, normalerweise eines der Datenelemente, die den größten Platz in der Klassendatei einnehmen. Darüber hinaus handelt es sich um den ersten Tabellentyp Datenelement, das in der Klassendatei erscheint. Im Konstantenpool werden hauptsächlich zwei Arten von Konstanten gespeichert: Literale und symbolische Referenzen. Literale ähneln eher dem Konzept von Konstanten auf Java-Sprachebene, z. B. Textzeichenfolgen, als endgültig deklarierte konstante Werte usw. Symbolreferenzen gehören zum Konzept der Kompilierungsprinzipien und umfassen hauptsächlich die folgenden Arten von Konstanten: · Von Modulen exportierte oder geöffnete Pakete · Vollqualifizierte Namen von Klassen und Schnittstellen· Feldname und Deskriptor (Deskriptor)
·Methodenname und Deskriptor·Methodenhandle und Methodentyp (Methodenhandle, Methodentyp, Invoke Dynamic)·Dynamisch berechnete Aufrufseite, dynamisch berechnete Konstante) Wenn Java-Code mit Javac kompiliert wird, ist dies nicht der Fall haben den Schritt „Verbindung“ wie C und C++. Stattdessen wird eine dynamische Verbindung durchgeführt, wenn die virtuelle Maschine die Klassendatei lädt (siehe Kapitel 7 für Details). Mit anderen Worten, die endgültigen Layoutinformationen jeder Methode und jedes Felds im Speicher werden nicht in der Klassendatei gespeichert. Wenn die symbolischen Referenzen dieser Felder und Methoden zur Laufzeit nicht von der virtuellen Maschine konvertiert werden, kann dies nicht der Fall sein erhalten, das heißt, es kann nicht direkt von der virtuellen Maschine verwendet werden. Wenn die virtuelle Maschine eine Klasse lädt, ruft sie die entsprechende Symbolreferenz aus dem Konstantenpool ab und analysiert und übersetzt sie dann beim Erstellen oder zur Laufzeit der Klasse in die spezifische Speicheradresse. Jede Konstante im Konstantenpool ist eine Tabelle. Ursprünglich gab es 11 Arten von Tabellenstrukturdaten mit unterschiedlichen Strukturen. Um dynamische Sprachaufrufe besser zu unterstützen, wurden später 4 zusätzliche dynamische sprachbezogene Konstanten hinzugefügt [1] Um das Java-Modulsystem (Jigsaw) zu unterstützen, wurden zwei Konstanten, CONSTANT_Module_info und CONSTANT_Package_info, hinzugefügt. Daher gibt es ab JDK13 17 verschiedene Arten von Konstanten in der Konstantentabelle.
Da die Methoden und Felder in der Klassendatei übrigens auf die Konstante CONSTANT_Utf8_info verweisen müssen, um den Namen zu beschreiben, ist die maximale Länge der Konstante CONSTANT_Utf8_info auch die maximale Länge der Methoden- und Feldnamen in Java. Die maximale Länge ist hier der maximale Längenwert, also der maximale Wert 65535, den der u2-Typ ausdrücken kann. Wenn daher ein Java-Programm einen Variablen- oder Methodennamen definiert, der mehr als 64 KB englischer Zeichen enthält, wird dieser nicht kompiliert, selbst wenn die Regeln und alle Zeichen zulässig sind.
Klassendatei /D:/BaiduYunDownload/geekbang-lessons/thinking-in-spring/validation/target/classes/org/geekbang/thinking/in/spring/validation/TestClass.class
Zuletzt geändert am 25.06.2020; Größe 439 Byte
Hauptversion: 52 Flags: ACC_PUBLIC, ACC_SUPERKonstanter Pool:
#1 = Methodref #4.#18 // java/lang/Object."
#2 = Fieldref #3.#19 // org/geekbang/thinking/in/spring /validation/TestClass.m:I
#3 = Klasse #20 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 =. Utf8
#12 = Utf8 dieses
#13 = Utf8 Lorg/geekbang/thinking/in/spring/validation/TestClass;
#14 = inc
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 TestClass.java
#18 = NameAndType #7:#8 // "
#19 = NameAndType #5: #6 // m:I
#20 = Utf8 org/geekbang/thinking/in/spring/validation/TestClass
#21 = Utf8 java/lang/Object
public.org.geekbang.thinking.in.spring.validation.Test Klasse(); # 1: invokespecial #1 // Methode java/lang/Object. "1: getfield #2 // Feld m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
Zeile 7: 0
LocalVariableTable:
Startlänge Slot Name Signature
0 7 0 this Lorg/geekbang/thinking/in/spring/validation/TestClass;
}
SourceFile: „TestClass.java“
Nach dem Ende des Konstantenpools repräsentieren die nächsten 2 Bytes die Zugriffsflags (access_flags ), wird dieses Flag verwendet, um einige Zugriffsinformationen auf Klassen- oder Schnittstellenebene zu identifizieren, einschließlich: ob diese Klasse eine Klasse oder eine Schnittstelle ist; ob sie als abstrakter Typ definiert ist; Klasse, ob sie als endgültig deklariert ist;
Der Klassenindex, der übergeordnete Klassenindex und der Schnittstellenindexsatz werden alle durch zwei Indexwerte vom Typ u2 dargestellt Zeigen Sie über CONSTANT_Class_info auf eine Klassendeskriptorkonstante vom Typ CONSTANT_Class_info. Der Indexwert in der Konstante vom Typ kann in der vollständig qualifizierten Namenszeichenfolge gefunden werden, die in der Konstante vom Typ CONSTANT_Utf8_info definiert ist.
Erst mit dem Aufkommen von Lambda-Ausdrücken und Schnittstellen-Standardmethoden in JDK 8 kam die InvokeDynamic-Anweisung in den von der Java-Sprache generierten Klassendateien ins Spiel.
Dieses in JDK 8 hinzugefügte Attribut ermöglicht es dem Compiler also,
(Fügen Sie beim Kompilieren den Parameter -parameters hinzu.) Der Methodenname wird ebenfalls in die Klassendatei geschrieben, und MethodParameters ist ein Attribut der Methodentabelle. Es befindet sich auf derselben Ebene wie das Code-Attribut und kann über die Reflection-API unter abgerufen werden Laufzeit.
·Laden Sie eine lokale Variable in den Operandenstapel: iload
·Speichern Sie einen Wert vom Operandenstapel in die lokale Variablentabelle: istore
·Laden Sie eine Konstante in den Operandenstapel: bipush
iload_
Klassenlebenszyklus
Laden-> Verbindung (Überprüfung, Vorbereitung, Analyse)->Initialisierung->Verwenden->Deinstallieren. Die Reihenfolge der fünf Phasen Laden, Verifizieren, Vorbereiten, Initialisieren und Entladen wird bestimmt. Der Ladevorgang des Typs muss Schritt für Schritt in dieser Reihenfolge beginnen, während die Analysephase nicht unbedingt erforderlich ist: Sie kann in einigen Fällen initialisiert werden Dies dient der Unterstützung der Laufzeitbindungsfunktion der Java-Sprache (auch als dynamische Bindung oder späte Bindung bezeichnet). public static final int value = 123;Beim Kompilieren generiert Javac das ConstantValue-Attribut für den Wert. Während der Vorbereitungsphase weist die virtuelle Maschine den Wert 123 basierend auf der Einstellung von Con-stantValue zu. Der Arbeitsprozess des übergeordneten Delegationsmodells ist: Wenn ein Klassenlader eine Klassenladeanforderung erhält, versucht er nicht, zuerst die Klasse selbst zu laden, sondern delegiert die Anforderung auf jeder Ebene an den übergeordneten Klassenlader, um sie abzuschließen Dies ist wahr für alle Klassenlader, daher sollten alle Ladeanforderungen schließlich an den Startklassenlader der obersten Ebene gesendet werden. Nur wenn der übergeordnete Lader meldet, dass er die Ladeanforderung nicht abschließen kann (die erforderliche Datei wird nicht in seinem Suchbereich gefunden). , versucht der Subloader, den Ladevorgang selbst abzuschließenZunächst wird der Erweiterungsklassenlader (Extension Class Loader) durch den Plattformklassenlader (Platform Class Loader) ersetzt. Dies ist eigentlich eine sehr logische Änderung. Da das gesamte JDK auf Modularität basiert (das ursprüngliche rt.jar und tools.jar wurden in Dutzende von JMOD-Dateien aufgeteilt), ist die Java-Klassenbibliothek natürlich ausreichend Es ist nicht erforderlich, das VerzeichnisAlle Dispatch-Aktionen, die auf statischen Typen basieren, um die Ausführungsversion einer Methode zu bestimmen, werden als statische Dispatch-Aktionen bezeichnet. Die typischste Anwendung des statischen Versands ist das Überladen von Methoden. Der statische Versand erfolgt während der Kompilierungsphase, sodass die Aktion zur Bestimmung des statischen Versands nicht tatsächlich von der virtuellen Maschine ausgeführt wird. Aus diesem Grund wird sie in einigen Materialien als „Analyse“ und nicht als „Versand“ klassifiziert.
Die Java Virtual Machine unterstützt die folgenden 5 Methoden zum Aufrufen von Bytecode-Anweisungen:
·invokestatic. Wird zum Aufrufen statischer Methoden verwendet.
·invokespecial. Wird zum Aufrufen der Instanzkonstruktormethode
·invokevirtual. Wird zum Aufrufen aller virtuellen Methoden verwendet.
·invokeinterface. Wird zum Aufrufen von Schnittstellenmethoden verwendet. Zur Laufzeit wird ein Objekt ermittelt, das die Schnittstelle implementiert.
·invokedynamic. Die vom Call-Site-Qualifizierer referenzierte Methode wird zur Laufzeit zunächst dynamisch aufgelöst und dann ausgeführt. Die Dispatch-Logik der ersten vier Aufrufanweisungen ist in der Java Virtual Machine verankert, während die Dispatch-Logik der invokedynamic-Anweisung durch die vom Benutzer festgelegte Boot-Methode bestimmt wird.
Solange die Methode durch die Anweisungen „invokestatic“ und „invokespecial“ aufgerufen werden kann, kann die eindeutige Aufrufversion in der Analysephase bestimmt werden. Es gibt vier Methoden in der Java-Sprache, die diese Bedingung erfüllen: statische Methoden, private Methoden, Instanzkonstruktoren In Verbindung mit der von final geänderten Methode (obwohl sie mit der invokevirtual-Anweisung aufgerufen wird) lösen diese fünf Methodenaufrufe den Symbolverweis in einen direkten Verweis auf die Methode auf, wenn die Klasse geladen wird. Diese Methoden werden zusammenfassend als „nicht-virtuelle Methode“ (nicht virtuelle Methode) bezeichnet, andere Methoden hingegen werden als „virtuelle Methode“ (virtuelle Methode) bezeichnet.
Der Parsing-Aufruf muss ein statischer Prozess sein, der während der Kompilierung vollständig bestimmt wird. Während der Parsing-Phase des Klassenladens werden alle beteiligten Symbolreferenzen in klare direkte Referenzen umgewandelt, und es besteht keine Notwendigkeit, ihn bis zur Laufzeit zu verzögern. Die andere Hauptform des Methodenaufrufs: Dispatch-Aufruf (Dispatch) ist viel komplizierter. Er kann je nach Anzahl der Fälle in Einzelversand und Mehrfachversand unterteilt werden. Die Kombination dieser beiden Arten von Versandmethoden stellt vier Versandkombinationen dar: statischer Einzelversand, statischer Mehrfachversand, dynamischer Einzelversand und dynamischer Mehrfachversand. Schauen wir uns an, wie der Methodenversand in der virtuellen Maschine durchgeführt wird.
Der Code definiert bewusst zwei Variablen mit demselben statischen Typ, aber unterschiedlichen tatsächlichen Typen, aber die virtuelle Maschine (oder genauer gesagt der Compiler) übergibt den Parameter
statischer Typanstelle des tatsächlichen Typs als beim Überladen von Beurteilungsbasis. Da der statische Typ zur Kompilierungszeit bekannt ist, bestimmt der Javac-Compiler während der Kompilierungsphase anhand des statischen Typs des Parameters, welche überladene Version verwendet wird. Daher wählt er sayHello (Human) als aufrufendes Ziel aus und schreibt die Symbolik Referenz dieser Methode in die Parameter der beiden invokevirtual-Anweisungen in der main()-Methode.
Alle Dispatch-Aktionen, die auf statischen Typen basieren, um die Ausführungsversion einer Methode zu bestimmen, werden als statische Dispatch-Aktionen bezeichnet.Die typischste Anwendung des statischen Versands ist das Überladen von Methoden. Der statische Versand erfolgt während der Kompilierungsphase, sodass die Aktion zur Bestimmung des statischen Versands nicht tatsächlich von der virtuellen Maschine ausgeführt wird. Aus diesem Grund wird sie in einigen Materialien als „Parsing“ und nicht als „Versand“ klassifiziert. Es ist ersichtlich, dass die Überlastpriorität von Parametern variabler Länge am niedrigsten ist. Felder nehmen niemals am Polymorphismus teil. Wenn eine Methode einer Klasse auf ein Feld mit einem bestimmten Namen zugreift, bezieht sich der Name auf das Feld, das die Klasse sehen kann.
Wichtige PunkteDas liegt genau daran, dass der erste Schritt bei der Ausführung der invokevirtual-Anweisung darin besteht, den tatsächlichen Typ des Empfängers zur Laufzeit zu bestimmen, sodass die invokevirtual-Anweisung in den beiden Aufrufen die symbolische Referenz des nicht auflöst Methode im Konstantenpool in eine direkte Referenz Das ist es. Die Methodenversion wird basierend auf dem tatsächlichen Typ des Methodenempfängers ausgewählt. Dieser Prozess ist die Essenz des Methodenumschreibens in der Java-Sprache. Wir nennen diesen Dispatch-Prozess, der die Ausführungsversion der Methode basierend auf dem tatsächlichen Typ zur Laufzeit bestimmt, dynamischen Dispatch. Die Wurzel des Polymorphismus liegt in der Ausführungslogik der virtuellen Methodenaufrufanweisung invokevirtual. Natürlich gilt die Schlussfolgerung, die wir ziehen, nur für Methoden und nicht für Felder, da Felder diese Anweisung nicht verwenden.
Die Java-Sprache ist eine statische Multi-Dispatch- und dynamische Single-Dispatch-Sprache.
Um die Programmimplementierung zu vereinfachen, sollten Methoden mit derselben Signatur in der virtuellen Methodentabelle der übergeordneten Klasse und der Unterklasse dieselbe Indexnummer haben. Auf diese Weise wird bei einer Typänderung nur die virtuelle Methodentabelle durchsucht up muss geändert werden, und dann werden die erforderlichen Eintragsadressen entsprechend den Indizes in verschiedenen virtuellen Methodentabellen konvertiert. Die virtuelle Methodentabelle wird im Allgemeinen während der Verbindungsphase des Klassenladens initialisiert. Nach der Vorbereitung der Anfangswerte der Variablen der Klasse initialisiert die virtuelle Maschine auch die virtuelle Methodentabelle der Klasse.
Dynamische Sprachunterstützung Die Anzahl der Bytecode-Befehlssätze der Java Virtual Machine Seit der Einführung der ersten Java Virtual Machine gab es in mehr als 20 Jahren nur einen neuen Befehl, nämlich den Bytecode-Befehlssatz mit der Veröffentlichung von JDK 7. Das erste neue Mitglied des Abschnittscodes ist die invokedynamic-Anweisung. Diese neu hinzugefügte Anweisung ist eine der Verbesserungen, die vorgenommen wurden, um das Projektziel von JDK 7 zu erreichen: Unterstützung für dynamisch typisierte Sprache (Dynamically Typed Language) zu implementieren. Sie ist auch eine technische Reserve für die reibungslose Implementierung von Lambda-Ausdrücken in JDK 8. Was ist eine dynamisch typisierte Sprache [1]? Das Hauptmerkmal einer dynamisch typisierten Sprache besteht darin, dass der Hauptprozess der Typprüfung zur Laufzeit und nicht zur Kompilierungszeit durchgeführt wird. Zu den häufig verwendeten Sprachen gehören: APL, Clojure, Erlang, Groovy , JavaScript, Lisp, Lua, PHP, Prolog, Python, Ruby, Smalltalk, Tcl und mehr. Im Gegensatz dazu sind Sprachen, die während der Kompilierung eine Typprüfung durchführen, wie C++ und Java, die am häufigsten verwendeten statisch typisierten Sprachen. Variablen haben keinen Typ, aber nur Variablenwerte haben Typen Die Bereitstellung direkter Unterstützung für dynamische Typen auf der Ebene der Java Virtual Machine ist zu einem Problem geworden, das für die Entwicklung der Java-Plattform gelöst werden muss Der JSR-292-Vorschlag in JDK 7 Der technische Hintergrund der invokedynamic-Anweisung und die Entstehung des java.lang.invoke-Pakets. Das neu hinzugefügte java.lang.invoke-Paket [1] ist ein wichtiger Teil von JSR 292. Der Hauptzweck dieses Pakets besteht darin, sich vor In einfach darauf zu verlassen Zusätzlich zur Verwendung symbolischer Referenzen zur Bestimmung der Zielmethode des Aufrufs wird ein neuer Mechanismus zur dynamischen Bestimmung der Zielmethode bereitgestellt, der als „Methodenhandle“ bezeichnet wird. ·Reflection- und MethodHandle-Mechanismen simulieren im Wesentlichen Methodenaufrufe, aber Reflection simuliert Methodenaufrufe auf der Java-Codeebene, während MethodHandle Methodenaufrufe auf der Bytecodeebene simuliert. In der Tomcat-Verzeichnisstruktur können Sie 3 Gruppen von Verzeichnissen einrichten (/common/*, /server/* und /shared/*), diese sind jedoch nicht unbedingt standardmäßig geöffnet, sondern möglicherweise nur (sofern das Verzeichnis /lib/* vorhanden ist) wird zum Speichern von Java-Klassenbibliotheken verwendet. Darüber hinaus sollte das Verzeichnis „/WEB-INF/*“ der Webanwendung selbst hinzugefügt werden, insgesamt 4 Gruppen. Platzieren Sie die Java-Klassenbibliothek in diesen vier Verzeichnisgruppen. Jede Gruppe hat eine unabhängige Bedeutung: ·Platzieren Sie sie im Verzeichnis /common. Die Klassenbibliothek kann von Tomcat und allen Webanwendungen genutzt werden. ·Platzieren Sie es im Verzeichnis /server. Die Klassenbibliothek kann von Tomcat verwendet werden und ist für alle Webanwendungen unsichtbar. ·Legen Sie es im Verzeichnis /shared ab. Die Klassenbibliothek kann von allen Webanwendungen genutzt werden, ist für Tomcat selbst jedoch nicht sichtbar. ·Platzieren Sie es im Verzeichnis /WebApp/WEB-INF. Die Klassenbibliothek kann nur von der Webanwendung verwendet werden und ist für Tomcat oder andere Webanwendungen nicht sichtbar. Um diese Verzeichnisstruktur zu unterstützen und die Klassenbibliotheken im Verzeichnis zu laden und zu isolieren, hat Tomcat mehrere Klassenlader angepasst, die gemäß dem klassischen übergeordneten Delegationsmodell implementiert sind# 🎜🎜##🎜🎜 # Common Class Loader, Catalina Class Loader (auch bekannt als Server Class Loader), Shared Class Loader und Webapp Class Loader sind selbstdefinierte Tomcat-Klassenlader, die die Java-Klassenbibliotheken laden in /common/*, /server/*, /shared/* bzw. /WebApp/WEB-INF/*. Normalerweise gibt es mehrere Instanzen von WebApp-Klassenladern und JSP-Klassenladern. Jede Webanwendung entspricht einem WebApp-Klassenlader und jede JSP-Datei entspricht einem JasperLoader-Klassenlader. Der Ladebereich von JasperLoader ist nur die von dieser JSP-Datei kompilierte Klassendatei. Der Zweck ihrer Existenz besteht darin, sie zu verwerfen: Wenn der Server erkennt, dass die JSP-Datei geändert wurde, wird sie ersetzt aktuell Eine Instanz von JasperLoader wird erstellt und ein neuer JSP-Klassenlader wird erstellt, um die HotSwap-Funktion der JSP-Datei zu implementieren.
Das obige ist der detaillierte Inhalt vonWas sind die Wissenspunkte zu Java-Klassendateien?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!