Mehr als nur Interviews – ausführliche Erläuterung der Interviewfragen zum Laden von JVM-Klassen

Interview Fragen

Lernen mit Fragen ist am effizientesten. Dieses Mal werden wir versuchen, die folgenden Fragen zu beantworten:

Was ist Klassenladen?
Welche Situationen lösen das Laden von Klassen aus?
Erzählen Sie mir etwas über den Prozess des JVM-Ladens einer Klasse.
Wann wird Speicher für Variablen zugewiesen?
Was ist der Klassenlademechanismus der JVM?
Kann der elterliche Delegationsmechanismus unterbrochen werden? Warum?
Die Antwort steht am Ende des Artikels. Wenn Sie keine Zeit haben, das Prinzip zu lesen, können Sie zum Ende springen und die Antwort direkt lesen.

Detaillierte Prinzipien
Klassenlebenszyklus
Ich glaube, jeder kennt den Lebenszyklus einer Klasse wie folgt:
Fügen Sie hier eine Bildbeschreibung ein

Aber dieses Ding wird nach dem Auswendiglernen immer vergessen und das Auswendiglernen nach dem Vergessen, genau wie Ma Hemei, oder?

Tatsächlich werden Sie es im Grunde nie wieder vergessen, wenn Sie es einmal verstanden haben.

Laden
Laden bewirkt hauptsächlich drei Dinge:

Suchen Sie die Klassendatei (erhalten Sie den binären Bytestrom, der diese Klasse definiert, über den vollständig qualifizierten Namen der Klasse)
und fügen Sie sie in den Methodenbereich ein (konvertieren Sie die durch diesen Bytestrom dargestellte statische Speicherstruktur in die Laufzeitdatenstruktur des Methodenbereichs).
So öffnen Sie einen Eintrag (Generieren Sie ein java.lang.Class-Objekt, das diese Klasse als Eintrag für den Zugriff auf diese Datenstrukturen im Methodenbereich darstellt.) Im
Allgemeinen besteht dieser Schritt darin, die Klasse über den Klassenlader in den Speicher einzulesen. Es ist zu beachten, dass das Objekt zwar im dritten Schritt generiert wird, sich jedoch nicht im Heap, sondern im Methodenbereich befindet.

Connection
Connection gliedert sich in drei Schritte. Im Allgemeinen wird bei Vorstellungsgesprächen eher nach der Vorbereitung gefragt.

Überprüfen Sie
, wie der Name schon sagt, ob die im Bytestrom der Klassendatei enthaltenen Informationen den Anforderungen der aktuellen virtuellen Maschine entsprechen.

Vorbereitung
In diesem Schritt werden statischen Variablen und statischen Konstanten Speicher zugewiesen und Werte zugewiesen.

Es ist zu beachten, dass statischen Variablen nur Standardwerte zugewiesen werden. Zum Beispiel Folgendes:

public static int value = 123;
Kopieren
Der dem Wert zu diesem Zeitpunkt zugewiesene Wert ist 0, nicht 123.

Statische Konstanten (geändert durch static final) werden direkt zugewiesen. Zum Beispiel Folgendes:

public static final int value = 123;
Kopieren
Der dem Wert zu diesem Zeitpunkt zugewiesene Wert ist 123.

Parsing
In der Parsing-Phase ersetzt JVM die symbolische Referenz des Konstantenpools durch eine direkte Referenz.

Nun... was ist ein konstanter Pool? Was ist eine symbolische Referenz? Was ist ein direktes Zitat?

Wir fügen den Konstantenpool in die JVM-Speicherstruktur ein. Lassen Sie mich zunächst darüber sprechen, was eine symbolische Referenz und eine direkte Referenz ist.

Symbolische Referenzen und direkte Referenzen
Angenommen, es gibt eine Worker-Klasse, die eine run()-Methode der Car-Klasse enthält, wie folgt:

class Worker{ ... public void gotoWork(){ car.run(); //Die binäre Darstellung dieses Codes in der Worker-Klasse ist eine symbolische Referenz} ...}
Kopieren
Vor der Analysephase ist dies bei der Worker-Klasse nicht der Fall wissen car.run() Wo ist der Speicher dieser Methode, daher kann nur eine Zeichenfolge zur Darstellung dieser Methode verwendet werden. Die Zeichenfolge enthält genügend Informationen wie Klasseninformationen, Methodennamen, Methodenparameter usw., damit der entsprechende Speicherort für die tatsächliche Verwendung gefunden werden kann.

Diese Zeichenfolge wird als Symbolreferenz bezeichnet.

In der Analysephase findet JVM die entsprechende Adresse im Speicherbereich entsprechend dem Inhalt der Zeichenfolge und ersetzt dann die symbolische Referenz durch einen Zeiger, ein Handle, einen Offset usw., die direkt auf das Ziel zeigen, und kann dann sein direkt verwendet.

Diese Zeiger, Handles und Offsets, die direkt auf das Ziel zeigen, werden als direkte Referenzen bezeichnet.

Initialisierung
Die Hauptaufgabe der Initialisierung der Klasse besteht darin, der statischen Variablen den vom Programm festgelegten Anfangswert zuzuweisen.

Denken Sie an die statische Variable oben:

public static int value = 123;
Kopieren
Nach diesem Schritt ist der Wert von value schließlich 123.

Die Zusammenfassung lautet wie folgt:
Fügen Sie hier eine Bildbeschreibung ein

Die Bedingungen für die Klasseninitialisierung
Die Java Virtual Machine Specification schreibt strikt vor, dass es nur fünf Situationen gibt, in denen eine Klasse initialisiert werden muss:

Verwenden Sie die neue Bytecode-Anweisung, um eine Instanz einer Klasse zu erstellen, oder verwenden Sie getstatic, putstatic, um den Wert eines statischen Felds zu lesen oder festzulegen (mit Ausnahme der im Konstantenpool platzierten Konstanten). Wenn Sie eine statische Methode aufrufen, muss die entsprechende Klasse aufgerufen werden initialisiert werden.
Wenn Sie die Methode des Pakets java.lang.reflect verwenden, um einen reflektierenden Aufruf der Klasse durchzuführen, muss die Klasse zuerst initialisiert werden, wenn sie noch nicht initialisiert wurde.
Wenn beim Initialisieren einer Klasse festgestellt wird, dass die übergeordnete Klasse nicht initialisiert wurde, wird zunächst die Initialisierung der übergeordneten Klasse ausgelöst.
Wenn die virtuelle Maschine gestartet wird, muss der Benutzer eine Hauptklasse angeben (die Klasse, die die Methode main () enthält), und die virtuelle Maschine initialisiert diese Klasse zunächst.
Wenn bei Verwendung der dynamischen Sprachunterstützung von jdk1.7 das endgültige Analyseergebnis einer java.lang.invoke.MethodHandle-Instanz das Methodenhandle von REF_getStatic, REF_putStatic, RE_invokeStatic ist und die diesem Methodenhandle entsprechende Klasse nicht initialisiert wurde, Sie müssen zuerst die Initialisierung auslösen.
Zusätzlich zu den oben genannten fünf Fällen löst kein anderer Fall die Initialisierung der Klasse aus.

Die folgenden Situationen lösen beispielsweise keine Klasseninitialisierung aus:

Rufen Sie die statischen Felder der übergeordneten Klasse über die Unterklasse auf. Zu diesem Zeitpunkt erfüllt die übergeordnete Klasse Bedingung 1, während die Unterklasse keine Bedingungen erfüllt. Es wird also nur die übergeordnete Klasse initialisiert.
Der Verweis auf eine Klasse über ein Array löst keine Klasseninitialisierung aus. Denn new ist ein Array, keine Klasse.
Die statische Konstante der aufrufenden Klasse löst nicht die Initialisierung der Klasse aus, da die statische Konstante während der Kompilierungsphase im Konstantenpool der aufrufenden Klasse gespeichert wird und nicht auf die Klasse verweist, die die Konstante definiert.
Klassenlademechanismus Klassenlader
Wie
oben erwähnt, muss die Ladephase „einen binären Bytestrom erhalten, der diese Klasse durch den vollständig qualifizierten Namen einer Klasse beschreibt“. Dies ist, was der Klassenlader tut.

Der JVM verfügt über drei Klassenlader, nämlich:

Starten Sie den Klassenlader.
Erweiterungsklassenlader.
Anwendungsklassenlader
Ihre Vererbungsbeziehung ist wie folgt:
Fügen Sie hier eine Bildbeschreibung ein

Elterndelegierung
Der Arbeitsprozess des Elterndelegierungsmechanismus ist wie folgt:

Der aktuelle ClassLoader fragt zunächst ab, ob diese Klasse von seinen bereits geladenen Klassen geladen wurde, und wenn sie bereits geladen wurde, gibt er direkt die ursprünglich geladene Klasse zurück. Jeder Klassenlader verfügt über einen eigenen Ladecache. Wenn eine Klasse geladen wird, wird sie in den Cache gestellt und kann beim nächsten Laden direkt zurückgegeben werden.
Wenn die geladene Klasse nicht im Cache des aktuellen Klassenladers gefunden wird, wird der Lader der übergeordneten Klasse damit beauftragt, sie zu laden. Der Lader der übergeordneten Klasse übernimmt dieselbe Strategie, überprüft zuerst seinen eigenen Cache und vertraut dann die übergeordnete Klasse der übergeordneten Klasse an bis zum Laden bootstrp ClassLoader.
Wenn nicht alle übergeordneten Klassenlader geladen sind, wird er vom aktuellen Klassenlader geladen und in seinen eigenen Cache gelegt, sodass er bei der nächsten Ladeanforderung direkt zurückkehrt.
Warum ist es so kompliziert? Sie kommen alleine nicht damit zurecht?

Die Vorteile der elterlichen Delegation sind folgende:

Doppelbelastung vermeiden. Wenn das übergeordnete Element die Klasse bereits geladen hat, muss der untergeordnete ClassLoader sie nicht erneut laden.
zur Sicherheit. Vermeiden Sie das Ersetzen von Kernklassen wie String.
Unterbrechen der elterlichen Delegation
Der Mechanismus der „elterlichen Delegation“ ist nur ein von Java empfohlener Mechanismus, kein obligatorischer Mechanismus.

Beispielsweise unterbricht JDBC den übergeordneten Delegationsmechanismus. Es ruft den Thread-Kontextlader über Thread.currentThread().getContextClassLoader() ab, um die Driver-Implementierungsklasse zu laden, wodurch der übergeordnete Delegierungsmechanismus unterbrochen wird.

Warum das so ist, darüber werde ich später sprechen.

Antwort
Jetzt können wir die am Anfang des Artikels gestellte Frage beantworten. Versuchen Sie, auf der Grundlage Ihres Verständnisses und ohne Auswendiglernen zu antworten.

Was ist Klassenladen?
Nachdem die JVM den Binärstrom der Klasse über den Klassennamen abgerufen hat, wird der Vorgang des Einfügens der Klasse in den Methodenbereich und des Erstellens des Eingabeobjekts als Klassenladen bezeichnet. Nach dem Laden wird die Klasse im Speicher abgelegt.
Welche Situationen lösen die Initialisierung einer Klasse aus?
Klassen werden in 5 Fällen initialisiert:
Erstens, wenn es sich bei dieser Klasse um eine Einstiegsklasse handelt, wird sie initialisiert.
Zweitens verwenden Sie new, um Objekte zu erstellen oder statische Variablen der Klasse aufzurufen, und die Klasse wird initialisiert. Aber statische Konstanten zählen nicht.
Drittens erhalten Sie die Klasse durch Reflektion, und die Klasse wird initialisiert.
Viertens: Wenn die Unterklasse initialisiert wird, wird auch ihre übergeordnete Klasse initialisiert.
Fünftens wird bei Verwendung der dynamischen Sprachunterstützung von jdk1.7 auch der Aufruf des statischen Handles initialisiert.
Erzählen Sie mir etwas über den Prozess des JVM-Ladens einer Klasse.
Gleich wie bei Frage 1. Hier können Sie den Interviewer aber auch fragen, ob er Fragen zum Lebenszyklus der Klasse stellen möchte. Wenn Sie nach dem Lebenszyklus einer Klasse fragen, können Sie antworten, dass es fünf Phasen „Laden, Verbinden, Initialisieren, Verwenden und Entladen“ gibt und die Verbindung in drei Phasen „Überprüfung, Vorbereitung usw.“ unterteilt werden kann Analyse".
Wann wird Speicher für Variablen zugewiesen?
Weisen Sie während der Vorbereitungsphase Speicher für statische Variablen zu.
Was ist der Klassenlademechanismus der JVM?
Beim übergeordneten Delegierungsmechanismus lässt der Klassenlader zunächst seine übergeordnete Klasse laden. Wenn die übergeordnete Klasse nicht geladen werden kann, lädt er sie selbst.
Kann der elterliche Delegationsmechanismus unterbrochen werden? Warum
kann es passieren, dass JDBC den übergeordneten Delegierungsmechanismus mithilfe des Thread-Kontextladers unterbricht? Der Grund dafür ist, dass JDBC nur eine Schnittstelle und keine Implementierung bereitstellt. Diese Frage findet sich in der zitierten Literatur.

Ich denke du magst

Origin blog.csdn.net/m0_54861649/article/details/126628764
Empfohlen
Rangfolge