Heim  >  Artikel  >  Java  >  Detaillierte Einführung in den JVM-Klassenlademechanismus (Codebeispiel)

Detaillierte Einführung in den JVM-Klassenlademechanismus (Codebeispiel)

不言
不言Original
2018-09-25 15:14:542186Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Einführung (Codebeispiel) zum JVM-Klassenlademechanismus. Ich hoffe, dass er für Freunde hilfreich ist.

1. Überblick

Java unterscheidet sich von herkömmlichen kompilierten Sprachen wie C/C++ und auch von dynamischen Skriptsprachen wie PHP. Man kann sagen, dass Java eine halbkompilierte Sprache ist. Die von uns geschriebene Klasse wird zunächst in eine .class-Datei kompiliert. Wenn diese Klasse dann verwendet wird, wird die dieser Klasse entsprechende .class-Datei in den Speicher geladen. Der Inhalt dieser .class wird über den JVM-Klassenlademechanismus in den Speicher geladen.

Die virtuelle Maschine lädt die die Klasse beschreibenden Daten aus der Klassendatei in den Speicher, überprüft die Daten, konvertiert, analysiert und initialisiert sie und bildet schließlich einen Java-Typ, der direkt von der virtuellen Maschine verwendet werden kann. Dies ist die Klasse des Lademechanismus.

2. Verschiedene Schritte des Klassenladens

Laden

Der erste Schritt des „Klassenladens“-Prozesses beim Laden , Während des Ladevorgangs muss die virtuelle Maschine die folgenden drei Dinge ausführen:

  1. Erhalten Sie den binären Bytestream, der diese Klasse definiert, über den vollständig qualifizierten Namen einer Klasse.

  2. Konvertieren Sie die durch diesen Bytestrom dargestellte statische Speicherstruktur in eine Laufzeitdatenstruktur im Methodenbereich.

  3. Generieren Sie ein java.lang.Class-Objekt, das diese Klasse im Speicher darstellt und als Zugriffseingang auf verschiedene Daten dieser Klasse im Methodenbereich dient.

Es ist erwähnenswert, dass die Ladephase mit dem vom System bereitgestellten Boot-Klassenlader oder einem benutzerdefinierten Klassenlader abgeschlossen werden kann. aber Dies ist bei Arrays nicht der Fall Die Array-Klasse selbst wird nicht durch Klassenladen erstellt, sondern direkt von der Java Virtual Machine. Der Elementtyp, in dem die Daten gespeichert werden, muss jedoch vom Klassenlader erstellt werden.

Die Ladephase und der Verbindungsteil der nächsten Phase sind verschachtelt, aber die Startzeit der Ladephase und der Verbindungsphase behalten weiterhin eine feste Reihenfolge bei.

Überprüfung

Die Überprüfung ist der erste Schritt in der Verbindungsphase. Der Zweck dieser Phase besteht darin, sicherzustellen, dass die im Bytestrom der Klassendatei enthaltenen Informationen vorhanden sind kombiniert mit den aktuellen Anforderungen der virtuellen Maschine und gefährdet nicht die Sicherheit der virtuellen Maschine selbst. Obwohl Vorgänge wie Array-Out-of-Bounds und zufällige Transformationen von Objekten vom Compiler abgelehnt werden, muss die .class-Datei nicht unbedingt aus Java-Quellcode kompiliert werden und kann daher auf andere Weise generiert werden der .class-Datei muss überprüft werden.

Die Bedeutung der Verifizierungsphase liegt auf der Hand. Ob diese Phase streng ist oder nicht, bestimmt direkt, ob die Java Virtual Machine Angriffen mit bösartigem Code standhalten kann Die Last macht einen erheblichen Teil des Klassenladesystems der virtuellen Maschine aus.

Im Großen und Ganzen kann die Verifizierungsphase grob in vier Teile von Verifizierungsaktionen unterteilt werden: Überprüfung des Dateiformats, Überprüfung der Metadaten, Überprüfung des Bytecodes und Überprüfung der Symbolreferenz.

  • Symbolüberprüfung: Der Hauptzweck besteht darin, sicherzustellen, dass der Eingabebytestrom korrekt analysiert und im Methodenbereich gespeichert werden kann und dass das Format die Anforderungen zur Beschreibung einer Java-Typinformation erfüllt . Dieser Teil basiert auf der Überprüfung des Binärstroms, der dann in den Speicher geladen wird, und die anschließende Überprüfung wird im Speicher durchgeführt.

  • Metadatenüberprüfung: Diese Überprüfung führt hauptsächlich eine semantische Überprüfung der Metadateninformationen der Klasse durch, um sicherzustellen, dass keine Metadateninformationen vorhanden sind, die nicht der Java-Sprachspezifikation entsprechen.

  • Bytecode-Überprüfung: Dieser Teil ist die komplexeste Phase der Überprüfungsphase. Der Hauptzweck besteht darin, durch Datenfluss- und Kontrollflussanalyse festzustellen, ob das Programm legal und logisch ist.

  • Überprüfung der Symbolreferenz: Die Symbolreferenz erfolgt, wenn die virtuelle Maschine die Symbolreferenz in eine direkte Referenz umwandelt, sodass die Analyseaktion normal ausgeführt werden kann.

Vorbereitung

Die Vorbereitungsphase ist die Phase, in der Speicher für formale Klassenvariablen (statische Variablen) und die Anfangswerte von zugewiesen wird Die Klassenvariablen werden festgelegt. Der verwendete Speicher wird im Methodenbereich zugewiesen. Es ist erwähnenswert, dass zu diesem Zeitpunkt nur Klassenvariablen (statische Variablen) zugewiesen werden, keine Instanzvariablen.

Normalerweise wird der Anfangswert einer Klassenvariablen festgelegt. Dieser Anfangswert bezieht sich auf den Standardwert des Datentyps, z. B. 0 für den Typ int. Wenn die Klassenvariable jedoch durch final geändert wird, ist die Situation anders. In diesem Fall wird der angegebene Wert direkt zugewiesen.

Parsing

Die Parsing-Phase ist der Prozess, bei dem die virtuelle Maschine Symbolreferenzen im Konstantenpool durch direkte Referenzen ersetzt. Hier finden Sie eine Erklärung, was eine symbolische Referenz und was eine direkte Referenz ist.

Symbolische Referenz: Eine symbolische Referenz verwendet eine Reihe von Symbolen, um das referenzierte Ziel zu beschreiben. Das Symbol kann jede Form eines Literals sein, solange das Ziel bei der Verwendung eindeutig lokalisiert werden kann.

Direkte Referenz: Eine direkte Referenz kann ein Zeiger auf das Ziel, ein relativer Offset oder ein Handle sein, der das Ziel indirekt lokalisieren kann.

Die Parsing-Aktion wird hauptsächlich für 7 Arten von Symbolreferenzen durchgeführt: Klassen oder Schnittstellen, Felder, Klassenmethoden, Schnittstellenmethoden, Methodentypen, Methodenhandles und Call-Site-Qualifizierer.

Initialisierung

Die Klasseninitialisierungsphase ist der letzte Schritt des Klassenladevorgangs. Abgesehen davon, dass Benutzeranwendungen während der Ladephase über einen benutzerdefinierten Klassenlader teilnehmen können, werden die verbleibenden Aktionen vollständig von diesem dominiert und gesteuert virtuelle Maschine. In der Initialisierungsphase wird der in der Klasse definierte Java-Code tatsächlich ausgeführt.

In der Vorbereitungsphase wurde den Variablen einmalig der vom System benötigte Anfangswert zugewiesen, und in der Initialisierungsphase werden Klassenvariablen und andere Ressourcen entsprechend dem vom Programmierer über das Programm erstellten Planbereich initialisiert .

3. Interessantes Codesegment

public class StaticTest
 {

     public static void main(String[] args)
     {
         staticFunction();
     }
  
     static StaticTest st = new StaticTest();
  
     static
     {
         System.out.println("1");
     }
  
     {
         System.out.println("2");
     }
  
     StaticTest()
     {
         System.out.println("3");
         System.out.println("a="+a+",b="+b);
     }
  
     public static void staticFunction(){
         System.out.println("4");
     }
  
     int a=110;
     static int b =112;
 }

Was ist das Ergebnis der Ausführung dieses Codes?

Die Antwort lautet:

2
3
a=110,b=0
1
4

Warum ist das so? Vielleicht möchten Sie über Folgendes nachdenken.

Um diesen Code zu verstehen, müssen Sie nicht nur den Klassenlademechanismus von Java verstehen, sondern auch die Initialisierungsphase verstehen. Die Initialisierung statischer Codeblöcke und statischer Mitgliedsvariablen hängt mit der Reihenfolge des Codes zusammen.

Der Prozess des Klassenladens ist: Laden -> Verbindung (Überprüfung, Vorbereitung, Analyse) -> Initialisierung.

1. In der Vorbereitungsphase wird der Standardwert für die Klassenvariable festgelegt, also im Fall eins: st=null, b=0,

2. Die Klasse wird zuerst ausgeführt Konstruktor,

Mit anderen Worten, es führt nur den statisch modifizierten Codeblock aus und weist den statisch modifizierten Variablen Werte zu. Die Ausführungsreihenfolge statisch geänderter Codeblöcke und Klassenvariablen basiert auf der Reihenfolge, in der sie in der Datei ausgeführt werden. Und static StaticTest st = new StaticTest() steht an erster Stelle, daher wird new StaticTest() ausgeführt, um das Objekt zu initialisieren

2.1 Während des Initialisierungsprozesses des Objekts werden die Mitgliedsvariablen (Codeblock) angezeigt ) und dann die Konstruktormethode ausführen. Wer sie zuerst deklariert, führt sie auch zuerst aus, also der erste Codeblock

2.2 Nachdem die Mitgliedsvariable ausgeführt wurde, wird die Konstruktormethode ausgeführt . Zu diesem Zeitpunkt endet der durch static StaticTest st = new StaticTest(); ausgelöste Initialisierungsprozess des nicht statischen Codes und dann die Initialisierung des statischen Codes geht weiter, also die Ausgabe 1.

4. Das Laden der gesamten Klasse endet hier, der Code wird ausgeführt und 4 wird ausgegeben.

Sehen Sie sich das nächste an:

public class StaticTest
 {

     public static void main(String[] args)
     {
         staticFunction();
     }
  
  
     static
     {
         System.out.println("1");
     }
  
     {
         System.out.println("2");
     }
  
     StaticTest()
     {
         System.out.println("3");
         System.out.println("a="+a+",b="+b);
     }
  
     public static void staticFunction(){
         System.out.println("4");
     }
  
     int a=110;
     static int b =112;
     static StaticTest st = new StaticTest();  //将这条语句放到最下面
 }

ändert einfach eine Anweisung und das Ergebnis dieses Codes ist

1
2
3
a=110,b=112
4

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in den JVM-Klassenlademechanismus (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
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