Der Lebensstil der Java-Klassendatei

Willkommen zu einer weiteren Folge von "Under the Hood". Im Artikel des letzten Monats habe ich die Java Virtual Machine (JVM) besprochen, den abstrakten Computer, für den alle Java-Programme kompiliert wurden. Wenn Sie mit der JVM nicht vertraut sind, sollten Sie den Artikel des letzten Monats vor diesem lesen. In diesem Artikel gebe ich einen Einblick in die grundlegende Struktur und den Lebensstil der Java-Klassendatei.

Geboren um zu reisen

Die Java-Klassendatei ist ein genau definiertes Format für kompiliertes Java. Java-Quellcode wird in Klassendateien kompiliert, die von jeder JVM geladen und ausgeführt werden können. Die Klassendateien können über ein Netzwerk übertragen werden, bevor sie von der JVM geladen werden.

Wenn Sie diesen Artikel über einen Java-fähigen Browser lesen, werden Klassendateien für das Simulations-Applet am Ende des Artikels gerade über das Internet auf Ihren Computer übertragen. Wenn Sie sie anhören möchten (und Ihr Computer über Audiofunktionen verfügt), drücken Sie die folgende Taste:

Sie benötigen einen Java-fähigen Browser, um dieses Applet anzuzeigen

Klingt so, als hätten sie Spaß, oder? Das liegt in ihrer Natur. Java-Klassendateien wurden so konzipiert, dass sie gut funktionieren. Sie sind plattformunabhängig und daher an mehr Orten willkommen. Sie enthalten Bytecodes, den kompakten Befehlssatz für die JVM, damit sie leicht reisen können. Java-Klassendateien zippen ständig mit rasender Geschwindigkeit durch Netzwerke, um zu JVMs auf der ganzen Welt zu gelangen.

Was ist in einer Klassendatei?

Die Java-Klassendatei enthält alles, was eine JVM über eine Java-Klasse oder -Schnittstelle wissen muss. In der Reihenfolge ihres Auftretens in der Klassendatei sind die Hauptkomponenten: Magie, Version, konstanter Pool, Zugriffsflags, diese Klasse, Superklasse, Schnittstellen, Felder, Methoden und Attribute.

In der Klassendatei gespeicherte Informationen variieren häufig in der Länge - das heißt, die tatsächliche Länge der Informationen kann vor dem Laden der Klassendatei nicht vorhergesagt werden. Beispielsweise kann die Anzahl der in der Methodenkomponente aufgelisteten Methoden zwischen den Klassendateien unterschiedlich sein, da dies von der Anzahl der im Quellcode definierten Methoden abhängt. Solche Informationen werden in der Klassendatei organisiert, indem den tatsächlichen Informationen ihre Größe oder Länge vorangestellt wird. Auf diese Weise wird beim Laden der Klasse durch die JVM zuerst die Größe der Informationen variabler Länge gelesen. Sobald die JVM die Größe kennt, kann sie die tatsächlichen Informationen korrekt einlesen.

Informationen werden im Allgemeinen in die Klassendatei geschrieben, ohne Leerzeichen oder Auffüllungen zwischen aufeinanderfolgenden Informationen. Alles ist an Bytegrenzen ausgerichtet. Dies hilft dabei, Klassendateien klein zu halten, damit sie beim Fliegen über Netzwerke aerodynamisch sind.

Die Reihenfolge der Klassendateikomponenten ist streng definiert, damit JVMs wissen, was beim Laden einer Klassendatei zu erwarten ist und wo sie zu erwarten sind. Beispielsweise weiß jede JVM, dass die ersten acht Bytes einer Klassendatei die Magie- und Versionsnummern enthalten, dass der Konstantenpool mit dem neunten Byte beginnt und dass die Zugriffsflags dem Konstantenpool folgen. Da der konstante Pool jedoch eine variable Länge hat, kennt er den genauen Aufenthaltsort der Zugriffsflags erst, wenn das Lesen des konstanten Pools abgeschlossen ist. Sobald das Lesen des konstanten Pools abgeschlossen ist, weiß es, dass die nächsten zwei Bytes die Zugriffsflags sind.

Magie und Versionsnummern

Die ersten vier Bytes jeder Klassendatei sind immer 0xCAFEBABE. Diese magische Zahl erleichtert die Identifizierung von Java-Klassendateien, da die Wahrscheinlichkeit gering ist, dass Nicht-Klassendateien mit denselben anfänglichen vier Bytes beginnen. Die Nummer heißt Magie, weil sie von den Dateiformat-Designern aus dem Hut gezogen werden kann. Die einzige Voraussetzung ist, dass es nicht bereits von einem anderen Dateiformat verwendet wird, das in der realen Welt auftreten kann. Laut Patrick Naughton, einem Schlüsselmitglied des ursprünglichen Java-Teams, wurde die magische Zahl gewählt, "lange bevor der Name Java jemals in Bezug auf diese Sprache ausgesprochen wurde. Wir suchten nach etwas Lustigem, Einzigartigem und leicht zu merkendem. Es ist." nur ein Zufall, dass OxCAFEBABE, ein schiefer Hinweis auf die niedlichen Baristas bei Peet's Coffee, den Namen Java vorwegnahm. "

Die zweiten vier Bytes der Klassendatei enthalten die Haupt- und Nebenversionsnummern. Diese Nummern geben die Version des Klassendateiformats an, an das sich eine bestimmte Klassendatei hält, und ermöglichen es JVMs, zu überprüfen, ob die Klassendatei ladbar ist. Jede JVM hat eine maximale Version, die geladen werden kann, und JVMs lehnen Klassendateien mit späteren Versionen ab.

Ständiger Pool

Die Klassendatei speichert Konstanten, die ihrer Klasse oder Schnittstelle zugeordnet sind, im Konstantenpool. Einige Konstanten, die im Pool herumtollen, sind Literalzeichenfolgen, endgültige Variablenwerte, Klassennamen, Schnittstellennamen, Variablennamen und -typen sowie Methodennamen und Signaturen. Verfahren Unterschrift ist ihr Rückgabetyp und Satz von Argumenttypen.

Der konstante Pool ist als Array von Elementen variabler Länge organisiert. Jede Konstante belegt ein Element im Array. In der gesamten Klassendatei wird auf Konstanten durch den Integer-Index verwiesen, der ihre Position im Array angibt. Die Anfangskonstante hat einen Index von eins, die zweite Konstante hat einen Index von zwei usw. Dem Array des Konstantenpools geht die Arraygröße voraus, sodass JVMs wissen, wie viele Konstanten beim Laden der Klassendatei zu erwarten sind.

Jedes Element des Konstantenpools beginnt mit einem Ein-Byte-Tag, das den Konstantentyp an dieser Position im Array angibt. Sobald eine JVM dieses Tag erfasst und interpretiert, weiß sie, was auf das Tag folgt. Wenn beispielsweise ein Tag angibt, dass die Konstante eine Zeichenfolge ist, erwartet die JVM, dass die nächsten zwei Bytes die Zeichenfolgenlänge haben. Nach dieser Länge von zwei Bytes erwartet die JVM, die Länge der Bytes zu finden , aus denen die Zeichen der Zeichenfolge bestehen.

Im Rest des Artikels bezeichne ich manchmal das n-te Element des Konstantenpool-Arrays als Konstante_Pool [n]. Dies ist insofern sinnvoll, als der konstante Pool wie ein Array organisiert ist. Beachten Sie jedoch, dass diese Elemente unterschiedliche Größen und Typen haben und dass das erste Element einen Index von eins hat.

Zugriffsflags

Die ersten zwei Bytes nach dem Konstantenpool, die Zugriffsflags, geben an, ob diese Datei eine Klasse oder eine Schnittstelle definiert, ob die Klasse oder Schnittstelle öffentlich oder abstrakt ist und (wenn es sich um eine Klasse und keine Schnittstelle handelt) ob die Klasse ist endgültig.

Diese Klasse

Die nächsten zwei Bytes, diese Klassenkomponente , sind ein Index für das konstante Poolarray. Die Konstante, auf die diese Klasse verweist, constant_pool [this_class], besteht aus zwei Teilen, einem Ein-Byte-Tag und einem Zwei-Byte-Namensindex. Das Tag entspricht CONSTANT_Class, einem Wert, der angibt, dass dieses Element Informationen zu einer Klasse oder Schnittstelle enthält. Constant_pool [name_index] ist eine Zeichenfolgenkonstante, die den Namen der Klasse oder Schnittstelle enthält.

Die Komponente this class gibt einen Einblick in die Verwendung des konstanten Pools. Diese Klasse selbst ist nur ein Index für den konstanten Pool. Wenn eine JVM nach const_pool [this_class] sucht, findet sie ein Element, das sich mit seinem Tag als CONSTANT_Class identifiziert. Die JVM weiß, dass CONSTANT_Class-Elemente nach ihrem Ein-Byte-Tag immer einen Zwei-Byte-Index im Konstantenpool haben, der als Namensindex bezeichnet wird. Es wird also nach const_pool [name_index] gesucht, um die Zeichenfolge zu erhalten, die den Namen der Klasse oder Schnittstelle enthält.

Super Klasse

Nach dieser Klassenkomponente folgt die Superklassenkomponente , ein weiterer Zwei-Byte-Index in den Konstantenpool. Constant_pool [super_class] ist ein CONSTANT_Class-Element, das auf den Namen der Superklasse verweist, von der diese Klasse abstammt.

Schnittstellen

Die Schnittstellenkomponente beginnt mit einer Zwei-Byte-Anzahl der Anzahl der Schnittstellen, die von der in der Datei definierten Klasse (oder Schnittstelle) implementiert wurden. Unmittelbar danach folgt ein Array, das für jede von der Klasse implementierte Schnittstelle einen Index im Konstantenpool enthält. Jede Schnittstelle wird durch ein CONSTANT_Class-Element im Konstantenpool dargestellt, das auf den Namen der Schnittstelle verweist.

Felder

Die Feldkomponente beginnt mit einer Zwei-Byte-Anzahl der Felder in dieser Klasse oder Schnittstelle. Ein Feld ist eine Instanz oder Klassenvariable der Klasse oder Schnittstelle. Nach der Zählung folgt ein Array von Strukturen variabler Länge, eine für jedes Feld. Jede Struktur zeigt Informationen zu einem Feld an, z. B. den Namen, den Typ des Felds und, falls es sich um eine endgültige Variable handelt, den konstanten Wert. Einige Informationen sind in der Struktur selbst enthalten, andere in konstanten Poolpositionen, auf die die Struktur zeigt.

Die einzigen Felder, die in der Liste angezeigt werden, sind diejenigen, die von der in der Datei definierten Klasse oder Schnittstelle deklariert wurden. In der Liste werden keine Felder angezeigt, die von Superklassen oder Superschnittstellen geerbt wurden.

Methoden

Die Methodenkomponente beginnt mit einer Zwei-Byte-Anzahl der Methoden in der Klasse oder Schnittstelle. Diese Anzahl umfasst nur die Methoden, die von dieser Klasse explizit definiert werden, keine Methoden, die möglicherweise von Oberklassen geerbt werden. Nach der Methodenanzahl folgen die Methoden selbst.

Die Struktur für jede Methode enthält mehrere Informationen über die Methode, einschließlich des Methodendeskriptors (Rückgabetyp und Argumentliste), der Anzahl der für die lokalen Variablen der Methode erforderlichen Stapelwörter und der maximalen Anzahl der für den Operanden der Methode erforderlichen Stapelwörter Stapel, eine Tabelle mit Ausnahmen, die von der Methode abgefangen wurden, die Bytecode-Sequenz und eine Zeilennummertabelle.

Attribute

Im hinteren Bereich befinden sich die Attribute, die allgemeine Informationen zu der in der Datei definierten Klasse oder Schnittstelle enthalten. Der Attributabschnitt enthält eine Zwei-Byte-Anzahl der Attribute, gefolgt von den Attributen selbst. Ein Attribut ist beispielsweise das Quellcode-Attribut. Es zeigt den Namen der Quelldatei an, aus der diese Klassendatei kompiliert wurde. JVMs ignorieren alle Attribute, die sie nicht erkennen, stillschweigend.

Wird geladen: Eine Simulation einer Klassendatei, die ihr JVM-Ziel erreicht

Das folgende Applet simuliert eine JVM, die eine Klassendatei lädt. Die in die Simulation geladene Klassendatei wurde vom Javac-Compiler mit dem folgenden Java-Quellcode generiert:

Klasse Act {public static void doMathForever () {int i = 0; während (wahr) {i + = 1; i * = 2; }}}

Der obige Codeausschnitt stammt aus dem Artikel des letzten Monats über die JVM. Es ist dieselbe doMathForever () -Methode, die vom EternalMath-Applet aus dem Artikel des letzten Monats ausgeführt wurde. Ich habe diesen Code gewählt, um ein echtes Beispiel zu liefern, das nicht zu komplex war. Obwohl der Code in der realen Welt möglicherweise nicht sehr nützlich ist, wird er in eine reale Klassendatei kompiliert, die von der folgenden Simulation geladen wird.

Mit dem GettingLoaded-Applet können Sie die Klassenlastsimulation schrittweise ausführen. Für jeden Schritt auf dem Weg können Sie den nächsten Teil der Bytes lesen, der von der JVM verbraucht und interpretiert werden soll. Drücken Sie einfach die "Step" -Taste, damit die JVM den nächsten Block verbraucht. Durch Drücken von "Zurück" wird der vorherige Schritt rückgängig gemacht, und durch Drücken von "Zurücksetzen" wird die Simulation in den ursprünglichen Zustand zurückversetzt, sodass Sie von vorne beginnen können.

Die JVM wird unten links angezeigt und verbraucht den Bytestrom, aus dem die Klassendatei Act.class besteht. Die Bytes werden in hexadezimalem Streaming von einem Server unten rechts angezeigt. Die Bytes wandern Stück für Stück von rechts nach links zwischen dem Server und der JVM. Der Teil der Bytes, der von der JVM beim nächsten Tastendruck "Schritt" verbraucht werden soll, wird rot angezeigt. Diese hervorgehobenen Bytes werden im großen Textbereich über der JVM beschrieben. Alle verbleibenden Bytes hinter dem nächsten Block werden schwarz angezeigt.

Ich habe versucht, jeden Teil der Bytes im Textbereich vollständig zu erklären. Der Textbereich enthält daher viele Details, und Sie möchten möglicherweise zuerst alle Schritte durchgehen, um sich einen Überblick zu verschaffen, und dann nach weiteren Details suchen.

Viel Spaß beim Klicken.

Sie benötigen einen Java-fähigen Browser, um dieses Applet anzuzeigen.

Klicken Sie hier, um den Quellcode von GettingLoaded anzuzeigen. Um dieses Applet selbst auszuführen, benötigen Sie außerdem die beiden Dateien, die dieses Applet vom Server abruft, die ASCII-Datei, die den Text für jeden Schritt enthält, und die Act.class-Datei selbst. Klicken Sie hier, um den Quellcode des Audio-Applets Flying Class Files anzuzeigen.

ENDNOTE: Das Kleingedruckte: "The Java Class File Lifestyle" Artikel Copyright (c) 1996 Bill Venners. Alle Rechte vorbehalten. Applet "GettingLoaded" Copyright (c) 1996 Artima Software Company. Alle Rechte vorbehalten.

: END_ENDNOTE

Bill Venners ist Präsident der Artima Software Company. Über Artima entwickelt und berät er kundenspezifische Software.

Erfahren Sie mehr über dieses Thema

  • Die Java Virtual Machine Specification, das offizielle Wort von Sun.

    //java.sun.com/1.0alpha3/doc/vmspec/vmspec_1.html

  • Wenn es herauskommt, ist das Buch The Java Virtual Machine Specification , //www.aw.com/cp/lindholm-yellin.html, von Tim Lindholm und Frank Yellin (ISBN 0-201-63452-X) Teil von The Java Die Serie //www.aw.com/cp/javaseries.html) von Addison-Wesley wird wahrscheinlich die beste JVM-Ressource sein.
  • Ein Entwurf von Kapitel 4 der Java Virtual Machine Specification , der das Klassendateiformat und den Bytecode-Verifizierer beschreibt, kann von JavaSoft abgerufen werden.

    //java.sun.com/java.sun.com/newdocs.html

Diese Geschichte "The Java Class File Lifestyle" wurde ursprünglich von JavaWorld veröffentlicht.