2. Welche Bereiche sind im Laufzeitdatenbereich enthalten?
3. Welche Daten werden im Stack bzw. Heap gespeichert?
4. Warum sollten wir die permanente Generation (PermGen) durch den Metaspace (MetaSpace) ersetzen?
5. Verstehen Sie die Grundstruktur des Heap-Speicherplatzes? Unter welchen Umständen gelangt ein Objekt in die alte Generation?
6. In welchem Speicherbereich werden große Objekte abgelegt?
7. Wie ist der Erstellungsprozess von Java-Objekten?
Frage Antwort
1. Aus welchen Teilen besteht JVM?
Antwort: JVM ist eine ausführbare DateiBytecode (.class) Datei eines virtuellen Computers, der auch Speicherverwaltung, Speicherbereinigung und andere Mechanismen bereitstellt. Es enthält die folgenden Hauptteile.
Subsystem zum Laden von Klassen: Verantwortlich für das Laden von Bytecode-Dateien (.class) in die JVM.
Laufzeitdatenbereich: ist der Speicherbereich, den die JVM während der Ausführung verwendet.
Ausführungs-Engine: Verantwortlich für die Interpretation oder Kompilierung von Bytecode in Maschinencode für die Prozessorausführung.
Native Bibliotheksschnittstelle: Bietet eine Reihe von APIs zum Aufrufen nativer Bibliotheken, die im Betriebssystem oder in anderen Sprachen geschrieben sind.
2. Welche Bereiche sind im Laufzeitdatenbereich enthalten?
Antwort: Der Laufzeitdatenbereich ist der Speicherbereich, der von der JVM beim Ausführen eines Java-Programms zugewiesen wird.
Programmzähler: Es handelt sich um einen kleinen Speicherplatz und die Adresse des Bytecode-Befehls, der gerade vom Thread ausgeführt wird. Wenn der Thread eine native Methode ausführt, ist der Wert dieses Zählers undefiniert.
Java Virtual Machine Stack: Jeder Thread erstellt beim Erstellen einen Virtual Machine Stack, der zum Speichern der lokalen Variablentabelle, des Operandenrahmens, der dynamischen Verknüpfung, der Methodenexitinformationen usw. des Threads verwendet wird. Der Stapel der Java Virtual Machine enthält mehrere Stapelrahmen. Der Prozess vom Aufruf jeder Methode bis zum Abschluss der Ausführung entspricht dem Prozess vom Verschieben eines Stapelrahmens in den Stapel in der virtuellen Maschine.
Nativer Methodenstapel: Dies ist der von der JVM für die Ausführung nativer Methoden vorbereitete Speicherplatz. Er hat ähnliche Funktionen wie der Java Virtual Machine Stack. Es handelt sich um ein Speichermodell, das den laufenden Prozess nativer Methoden beschreibt.
Heap: Wird zum Speichern fast aller Objektinstanzen und Arrays verwendet und ist der Hauptbereich, in dem der Garbage Collector arbeitet.
Methodenbereich: Wird zum Speichern von Klasseninformationen, Konstanten, statischen Variablen, vom Just-in-Time-Compiler kompiliertem Code usw. verwendet, der von der JVM geladen wird. Vor JDK1.8 wurde es als permanente Generation implementiert. Ab JDK1.8 wird die permanente Generation durch den ursprünglichen Speicherplatz ersetzt. Metaspace verwendet lokalen Speicher anstelle von Heap-Speicher.
3. Welche Daten werden im Stack bzw. Heap gespeichert?
Antwort: Im Stapel gespeicherte Daten (Java Virtual Machine Stack):
Lokale Variablentabelle: Wird hauptsächlich zum Speichern von Methodenparametern und lokalen Variablen innerhalb der Methode verwendet. Zu den Datentypen gehören grundlegende Datentypen und Objektreferenzen.
Operandenstapel: Wird zum vorübergehenden Speichern von Betriebsanweisungen und Zwischenergebnissen während der Methodenausführung verwendet.
Dynamischer Link: Ein Verweis auf den Konstantenpool der Klasse, zu der die Methode gehört, der zum Auflösen von Symbolverweisen in der Methode verwendet wird.
Methodenrückgabeadresse: Speichert die Adresse der nächsten Anweisung, die nach dem Methodenaufruf ausgeführt wird. Im Heap gespeicherte Daten:
Objektinstanz: Eine durch das Schlüsselwort new im Programm erstellte Objektinstanz, einschließlich der Eigenschaften und Methoden des Objekts.
Array: Alle Arten von Arrays, einschließlich Basistyp-Arrays und Objekt-Arrays.
4. Warum sollten wir die permanente Generation (PermGen) durch den Metaspace (MetaSpace) ersetzen?
Antwort: Das Ersetzen der permanenten Generation durch Metaspace dient hauptsächlich dazu, einige inhärente Probleme und Einschränkungen der permanenten Generation zu lösen und die Leistung und Flexibilität der JVM zu verbessern.
Verbessern Sie die Flexibilität und Effizienz der Speicherverwaltung: Die Speichergröße der permanenten Generation wird beim Start der JVM festgelegt und kann nicht dynamisch angepasst werden. Metaspace verwendet lokalen Speicher anstelle von Java-Heap-Speicher und seine Größe kann je nach Bedarf dynamisch angepasst werden.
Lösen Sie das Problem des Klassenentladens und der Müllsammlung: Das GC-Verhalten der permanenten Generation ist komplex und unvorhersehbar, und die Recyclingeffizienz ist gering.
Bieten Sie bessere Leistung und Stabilität: Durch die Verwendung von Metaspace wird die JVM-Speicherverwaltung einheitlicher und konsistenter, da Metaspace wie andere Speicherbereiche über lokalen Speicher verwaltet wird. Dies vereinfacht Speicherverwaltungsstrategien und verbessert die Gesamtleistung und Stabilität.
Vereinfachen Sie die JVM-Speicherverwaltung
5. Verstehen Sie die Grundstruktur des Heap-Speicherplatzes? Unter welchen Umständen gelangt ein Objekt in die alte Generation?
Antwort: Die Grundstruktur des Heap-Speicherplatzes besteht hauptsächlich aus der neuen Generation, der alten Generation und der permanenten Generation. Nach JDK8 wird die permanente Generation durch Metaspace ersetzt und verwendet lokalen Speicher zur Speicherung.
Känozoische Generation: Der Fortschritt der neuen Generation ist in das Eden-Gebiet und zwei Überlebensgebiete (Survivor 0 und Survivor 1) unterteilt.
Eden-Bereich: Neu erstellte Objekte reservieren zunächst Speicher im Eden-Bereich.
Survivor-Bereich (S0, S1): Wird zum Speichern von Objekten verwendet, die die Garbage Collection der neuen Generation überstanden haben. Nach jedem Minor GC werden überlebende Objekte zwischen diesen beiden Bereichen hin und her kopiert.
Alte Generation: Objekte, die nach mehreren Minor GCs noch am Leben sind. Die Garbage Collection (Major GC oder Full GC) wird in der alten Generation seltener durchgeführt.
Permanente Generierung/Metaraum: Wird zum Speichern von Metadaten von Klassen verwendet, einschließlich Klassendefinitionen, Konstanten, statischen Variablen, Just-in-Time-kompiliertem Code usw.
Die Situation, wenn sich das Objekt in der alten Generation befindet:
Der Altersschwellenwert ist erreicht: Jedes Objekt hat ein Alter, wenn in der neuen Generation Speicher zugewiesen wird, und das Alter wird nach jedem Minor GC um 1 erhöht. Wenn das Alter einen bestimmten Schwellenwert erreicht (Standard ist 15), wird das Objekt auf die alte Generation heraufgestuft.
Großes Objekt: Wenn das Objekt zu groß ist und den von der JVM festgelegten Schwellenwert überschreitet, weist das Objekt direkt Speicherplatz in der alten Generation zu.
Unzureichender Platz im Survivor-Bereich: Wenn der Survivor-Bereich während der Minor GC nicht über genügend Platz verfügt, um alle überlebenden Objekte unterzubringen, werden diese Objekte vorhanden sein
Dynamische Bestimmung des Objektalters: Wenn die Größe aller gleichaltrigen Objekte im Survivor-Raum die Hälfte des Survivor-Raums überschreitet, können Objekte, deren Alter größer oder gleich diesem Alter ist, direkt in die alte Generation eintreten.
// 动态年龄计算代码
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity){//survivor_capacity是survivor空间的大小size_t desired_survivor_size =(size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);//TargetSurvivorRatio 为50size_t total =0;
uint age =1;while(age < table_size){
total += sizes[age];//sizes数组是每个年龄段对象大小if(total > desired_survivor_size)break;
age++;}
uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;...}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
6. In welchem Speicherbereich werden große Objekte abgelegt?
Antwort: Große Objekte (sehr große Arrays und Zeichenfolgen) werden normalerweise direkt im Speicherbereich der alten Generation zugewiesen.Dies soll verhindern, dass die neue Generation häufig auftrittMüllabfuhrZu diesem Zeitpunkt werden häufig große Objekte zwischen dem Eden-Bereich und dem Survivor-Bereich kopiert, wodurch die Effizienz der Speicherbereinigung verbessert wird. Konfigurieren Sie den Schwellenwert für große Objekte, um direkt in die alte Generation einzutreten:
7. Wie ist der Erstellungsprozess von Java-Objekten?
Antwort:
Klassenladeprüfung
Wenn die Klasse nicht geladen, verbunden und initialisiert wurde, lädt die JVM die Klasse zuerst. Dies umfasst die folgenden Schritte:
Laden: Lesen Sie die Klassendatei über den Klassenlader und laden Sie den Bytecode der Klasse in den Speicher.
Verbindung: umfasst drei Phasen: Überprüfung, Vorbereitung und Analyse. Überprüfen Sie die Richtigkeit der Klassendateien, bereiten Sie statische Variablen der Klasse vor, weisen Sie Speicher zu und lösen Sie Symbolverweise in direkte Verweise auf.
Initialisierung: Führen Sie den statischen Initialisierungsblock der Klasse und die Initialisierung statischer Variablen aus.
Speicherzuweisung
JVM weist Speicher für neue Objekte im Heap zu. Die Größe des zugewiesenen Speichers wird durch die Struktur des Objekts bestimmt, einschließlich des Objektheaders und der Instanzdaten.
Die JVM verfügt im Wesentlichen über zwei Möglichkeiten, Speicher zuzuweisen:
Bump-the-Pointer: Wenn der Heap-Speicher regulär ist, muss sich der Zuordnungszeiger nur um eine bestimmte Distanz zum freien Speicherbereich bewegen.
Freie Liste: Wenn der Heap-Speicher unregelmäßig ist, muss die JVM eine freie Liste verwalten und beim Zuweisen von Speicher den entsprechenden Block aus der freien Liste finden.
Auf Nullwert initialisieren
Die JVM initialisiert alle Instanzvariablen des Objekts auf ihre Standardwerte. Beispielsweise werden Variablen vom numerischen Typ auf 0, Variablen vom booleschen Typ auf „false“ und Variablen vom Referenztyp auf „null“ initialisiert.
Objektkopf festlegen
Legen Sie die Objekt-Header-Informationen im Speicherbereich des Objekts fest, einschließlich des Hash-Codes des Objekts, des GC-Generierungsalters, des Sperrstatus-Flags, der vom Thread gehaltenen Sperre, der voreingenommenen Thread-ID usw.
Konstruktorinitialisierung
Rufen Sie den Konstruktor des Objekts auf, um die Initialisierung des Objekts abzuschließen. Dazu gehört die Durchführung expliziter Initialisierungsvorgänge für Instanzvariablen sowie Code im Konstruktorkörper. Konkrete Schritte sind wie folgt:
Führen Sie den Instanzinitialisierungsblock der Klasse aus.
Führen Sie die Konstruktormethode der übergeordneten Klasse von oben nach unten gemäß der Vererbungshierarchie aus.
Initialisieren Sie Instanzvariablen auf explizit angegebene Werte.
Führen Sie den Hauptteil der Konstruktormethode der Klasse aus.