Heim  >  Artikel  >  Java  >  Detaillierte Einführung in Java-Speicherbereiche und Speicherüberlaufausnahmen

Detaillierte Einführung in Java-Speicherbereiche und Speicherüberlaufausnahmen

黄舟
黄舟Original
2017-03-17 10:18:411336Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen zum Java-Speicherbereich und zur Speicherüberlaufausnahme vorgestellt. Freunde, die sie benötigen, können sich auf

Java-Speicherbereich- und Speicherüberlaufausnahme

beziehen Übersicht

Für Entwickler von C- und C++-Programmentwicklungen haben Programmierer im Bereich der Speicherverwaltung das uneingeschränkte Recht, Speicher zu nutzen, aber sie auch Die Hauptsache ist Speicher richtig zu nutzen und zu bereinigen, was von Programmierern ein hohes Niveau erfordert.

Für Java-Programmierer besteht mit Hilfe des automatischen Speicherverwaltungsmechanismus der virtuellen Maschine keine Notwendigkeit, für jeden neuen Vorgang gepaarten Lösch-/Freigabecode zu schreiben, und es scheint, dass es zu Speicherlecks und Speicherüberlaufproblemen kommt dass mit der virtuellen Maschine, die den Speicher verwaltet, alles in Ordnung ist. Dies liegt jedoch daran, dass Java-Programmierer der virtuellen Java-Maschine die Möglichkeit gegeben haben, den Speicher zu steuern. Wenn Probleme mit Speicherlecks und -überläufen auftreten, wird die Fehlerbehebung zu einer äußerst schwierigen Aufgabe arbeiten.

Java-Laufzeitdatenbereich

Wir gehen im Allgemeinen davon aus, dass die JVM nur aus zwei Teilen besteht: dem Heap und dem Stack, aber die eigentliche Java Virtual Machine führt Java aus Dabei wird der von ihm verwaltete Speicher in mehrere unterschiedliche Datenbereiche aufgeteilt. Diese Bereiche haben ihre eigenen Zwecke sowie den Zeitpunkt der Erstellung und Zerstörung. Einige Bereiche existieren beim Start des Prozesses der virtuellen Maschine, und einige Bereiche werden abhängig vom Start und Ende des Benutzer-Threads erstellt und zerstört. Wie unten gezeigt:

Programmzähler

Wenn Sie die Prinzipien der Computerkomposition studiert haben, sollte klar sein, dass das Programm Der Zähler entspricht einer ID-Karte. Da die JVM auch über eine eigene CPU verfügt, plant sie bei der Ausführung eines Multithread-Programms die Ausführung von Threads anhand des Programmzählers durch Zeitscheibenrotation.

Das Programmzählerregister ist ein kleiner Speicherplatz. Seine Funktion kann als Zeilennummeranzeige des vom aktuellen Thread ausgeführten Bytecodes betrachtet werden. Im konzeptionellen Modell der virtuellen Maschine (nur ein konzeptionelles Modell, verschiedene virtuelle Maschinen können auf effizientere Weise implementiert werden) funktioniert der Bytecode-Interpreter, indem er den Wert dieses Zählers ändert, um den nächsten Schritt auszuwählen, der ausgeführt werden muss , Verzweigungen, Schleifen, Sprünge, Ausnahmebehandlung, Thread-Wiederherstellung und andere grundlegende Funktionen müssen alle auf diesen Zähler angewiesen sein, um ausgeführt zu werden.

Da das Multithreading der Java Virtual Machine durch abwechselndes Wechseln von Threads und Zuweisen von Prozessorausführungszeit implementiert wird, führt ein Prozessor (bei einem Multi-Core-Prozessor ein Kern) zu jedem bestimmten Zeitpunkt nur aus Anleitung in einem Thread. Um die korrekte Ausführungsposition nach dem Threadwechsel wiederherzustellen, muss daher jeder Thread über einen unabhängigen Programmzähler verfügen. Die Zähler zwischen den einzelnen Threads beeinflussen sich nicht gegenseitig und werden unabhängig voneinander gespeichert. . "Erinnerung.

Wenn der Thread eine Java-Methode ausführt, zeichnet dieser Zähler die Adresse der ausgeführten Bytecode-Anweisung der virtuellen Maschine auf. Wenn der Thread eine Natvie-Methode ausführt, ist der Zählerwert leer (undefiniert). Dieser Speicherbereich ist der einzige Bereich, der in der Java Virtual Machine Specification keine OutOfMemoryError-Bedingungen angibt.

Java Virtual Machine Stack

Wie der Programmzähler sind auch Java Virtual Machine Stacks Thread-privat und ihr Lebenszyklusentspricht dem Threading .

Der Stapel der virtuellen Maschine beschreibt das Speichermodell der Java-Methodenausführung: Wenn jede Methode ausgeführt wird, wird gleichzeitig ein Stapelrahmen (Stack Frame) erstellt, um lokale Variablen Tabellen, Operationsstapel, dynamische Links, Methodenexporte und andere Informationen. Der Prozess vom Aufruf jeder Methode bis zum Abschluss der Ausführung entspricht dem Prozess, bei dem ein Stapelrahmen vom Stapel verschoben und aus dem Stapel im Stapel der virtuellen Maschine herausgenommen wird.

Manche Leute unterteilen den Java-Speicher oft in Heap-Speicher (Heap) und Stapelspeicher (Stack). Die Aufteilung der Java-Speicherbereiche ist tatsächlich weitaus komplizierter. Die Beliebtheit dieser Teilungsmethode zeigt nur, dass die Speicherbereiche, denen die meisten Programmierer am meisten Aufmerksamkeit schenken und die am engsten mit der Objektspeicherzuweisung zusammenhängen, diese beiden Bereiche sind. Der „Heap“, auf den Bezug genommen wird, wird später genauer besprochen, und der „Stapel“, auf den Bezug genommen wird, ist der Stapel der virtuellen Maschine, über den wir jetzt sprechen, oder der Teil der lokalen Variablentabelle des Stapels der virtuellen Maschine.

Die lokale Variablentabelle speichert verschiedene grundlegende Datentypen (boolean, byte, char, short, int, float, long, double), Objektreferenzen (Referenztyp). entspricht nicht dem Objekt selbst. Je nach Implementierung der virtuellen Maschine kann es sich um einen Referenzzeiger handeln, der auf die Startadresse des Objekts zeigt, oder er kann auf ein Handle verweisen, das das Objekt oder andere mit diesem Objekt verbundene Orte darstellt (Zeigt auf die Adresse einer Bytecode-Anweisung).

Die 64-Bit-langen und doppelten Datentypen belegen 2 lokale Variablenräume (Slots), und die anderen Datentypen belegen nur 1. Der von der lokalen Variablentabelle benötigte Speicherplatz wird während der Kompilierung zugewiesen. Bei der Eingabe einer Methode wird vollständig bestimmt, wie viel lokalen Variablenspeicher diese Methode im Frame zuweisen muss. Die Größe der lokalen Variablentabelle ändert sich während der Ausführung nicht der Methode.

In der Java Virtual Machine-Spezifikation sind für diesen Bereich zwei Ausnahmebedingungen angegeben: Wenn die vom Thread angeforderte Stapeltiefe größer ist als die von der virtuellen Maschine zulässige Tiefe, wird eine StackOverflowError-Ausnahme ausgelöst Der Stapel virtueller Maschinen kann dynamisch erweitert werden (Die meisten aktuellen virtuellen Java-Maschinen können dynamisch erweitert werden, die Java-Spezifikation für virtuelle Maschinen erlaubt jedoch auch Stapel virtueller Maschinen mit fester Länge.) Wenn die Erweiterung nicht für genügend Speicher angewendet werden kann, wird eine OutOfMemoryError-Ausnahme ausgelöst.

Nativer Methodenstapel

Die Funktionen von nativen Methodenstapeln (Native Method Stacks) und virtuellen Maschinenstapeln sind sehr ähnlich. Der einzige Unterschied besteht darin, dass der virtuelle Maschinenstapel sehr ähnlich ist ist Die virtuelle Maschine führt Java-Methodendienste (dh Bytecode-Dienste) aus, und der lokale Methodenstapel bedient die von der virtuellen Maschine verwendeten nativen Methoden. Die Spezifikation der virtuellen Maschine schreibt die Sprache, Verwendung und Datenstruktur der Methoden im lokalen Methodenstapel nicht vor, sodass bestimmte virtuelle Maschinen sie frei implementieren können. Einige virtuelle Maschinen (z. B. die virtuelle Sun HotSpot-Maschine) kombinieren sogar direkt den lokalen Methodenstapel und den Stapel der virtuellen Maschine in einem. Wie der Stapel der virtuellen Maschine löst auch der Stapelbereich der lokalen Methode die Ausnahmen StackOverflowError und OutOfMemoryError aus.

Java Heap

Für die meisten Anwendungen ist der Java Heap (Java Heap) der größte Speicherbereich, der von der Java Virtual Machine verwaltet wird. Der Java-Heap ist ein von allen Threads gemeinsam genutzter Speicherbereich und wird beim Start der virtuellen Maschine erstellt. Der einzige Zweck dieses Speicherbereichs besteht darin, Objektinstanzen zu speichern, und fast alle Objektinstanzen weisen hier Speicher zu. Dies wird in der Java Virtual Machine-Spezifikation wie folgt beschrieben: Alle Objektinstanzen und Arrays müssen auf dem Heap zugewiesen werden. Mit der Entwicklung von JIT-Compilern und der allmählichen Reife der Escape-Analysetechnologie müssen jedoch die Zuordnung auf dem Stapel und die Skalarersatzoptimierungstechnologie erfolgen wird Dies führt zu einigen subtilen Änderungen und alle auf dem Heap zugewiesenen Objekte werden allmählich weniger „absolut“.

Der Ava-Heap ist der Hauptbereich, der vom Garbage Collector verwaltet wird, daher wird er oft als „GC-Heap“ bezeichnet („Garbage Collected Heap“, glücklicherweise wird er in China nicht als „Müllhaufen“ übersetzt). Aus der Perspektive des Speicherrecyclings kann der Java-Heap auch unterteilt werden, da aktuelle Collectors grundsätzlich Generations-Collection-Algorithmen verwenden: Neue Generation und alte Generation, detailliertere umfassen Eden Space, From Survivor Space, To Survivor Space usw. Aus Sicht der Speicherzuweisung kann der Thread-gemeinsam genutzte Java-Heap in mehrere Thread-private Zuweisungspuffer (Thread Local Allocation Buffer, TLAB) unterteilt werden. Unabhängig davon, wie die Aufteilung erfolgt, hat dies jedoch nichts mit dem Speicherinhalt zu tun. Der Zweck der weiteren Aufteilung besteht darin, den Speicher besser zu recyceln oder Speicher schneller zuzuweisen. In diesem Kapitel diskutieren wir nur die Rolle des Speicherbereichs. Die Einzelheiten der Zuweisung und Wiederverwendung der oben genannten Bereiche im Java-Heap werden Gegenstand des nächsten Kapitels sein.

Gemäß der Java Virtual Machine Specification kann sich der Java-Heap in einem physisch diskontinuierlichen Speicherbereich befinden, solange er logisch kontinuierlich ist, genau wie unser Festplattenspeicher. Bei der Implementierung kann es entweder als feste Größe oder als skalierbar implementiert werden, aber die aktuellen gängigen virtuellen Maschinen werden alle als skalierbar implementiert (gesteuert durch -Xmx und -Xms). Wenn im Heap kein Speicher zum Abschließen der Instanzzuweisung vorhanden ist und der Heap nicht mehr erweitert werden kann, wird eine OutOfMemoryError-Ausnahme ausgelöst.

Methodenbereich

Der Methodenbereich ist wie der Java-Heap ein Speicherbereich, der von jedem Thread gemeinsam genutzt wird. Er wird zum Speichern von Klassen verwendet, die vom virtuellen geladen wurden Maschine. Informationen, Konstanten, statische Variablen, vom Just-in-Time-Compiler kompilierter Code und andere Daten. Obwohl die Java Virtual Machine-Spezifikation den Methodenbereich als logischen Teil des Heaps beschreibt, gibt es einen Alias ​​namens Non-Heap (Nicht-Heap), der vom Java-Heap unterschieden werden sollte.

Die Java Virtual Machine-Spezifikation weist in diesem Bereich sehr lockere Einschränkungen auf. Sie erfordern nicht nur keinen zusammenhängenden Speicher wie der Java-Heap und haben die Möglichkeit einer festen Größe oder Erweiterbarkeit, Sie können sich auch dafür entscheiden, keine Garbage Collection zu implementieren. Relativ gesehen ist das Garbage-Collection--Verhalten in diesem Bereich relativ selten, bedeutet aber nicht, dass die Daten in den Methodenbereich gelangen und „permanent“ existieren, wie der Name der permanenten Generation. Das Ziel des Speicherrecyclings in diesem Bereich besteht hauptsächlich darin, konstante Pools zu recyceln und Typen zu entladen. Im Allgemeinen sind die Recycling-„Ergebnisse“ in diesem Bereich relativ unbefriedigend, insbesondere beim Entladen von Typen In diesem Teil des Gebiets ist Recycling tatsächlich notwendig.

Gemäß der Java Virtual Machine-Spezifikation wird eine OutOfMemoryError-Ausnahme ausgelöst, wenn der Methodenbereich die Speicherzuweisungsanforderungen nicht erfüllen kann.

Runtime Constant Pool

Der Runtime Constant Pool ist Teil des Methodenbereichs. Neben Informationen wie der Klassenversion, Feldern, Methoden, Schnittstellen und anderen Beschreibungen in der Class-Datei gibt es auch einen Konstantenpool (Constant Pool Table), der zur Speicherung verschiedener dabei generierter Literale verwendet wird Kompilierung und Symbolreferenzen, die nach dem Laden der Klasse im Laufzeitkonstantenpool im Methodenbereich gespeichert werden.

Die Java Virtual Machine hat strenge Vorschriften für das Format jedes Teils der Klassendatei (natürlich einschließlich des Konstantenpools). Jedes Byte muss zum Speichern verwendet werden, welche Art von Daten den Spezifikationsanforderungen entsprechen müssen Das wird von der virtuellen Maschine erkannt, geladen und ausgeführt. Für den Laufzeitkonstantenpool stellt die Java Virtual Machine-Spezifikation jedoch keine detaillierten Anforderungen. Virtuelle Maschinen, die von verschiedenen Anbietern implementiert werden, können diesen Speicherbereich entsprechend ihren eigenen Anforderungen implementieren. Im Allgemeinen werden jedoch zusätzlich zum Speichern der in der Klassendatei beschriebenen Symbolreferenzen auch die übersetzten direkten Referenzen im Laufzeitkonstantenpool gespeichert.

Ein weiteres wichtiges Merkmal des Laufzeitkonstantenpools im Vergleich zum Klassendateikonstantenpool ist, dass er dynamisch ist. Die Java-Sprache erfordert nicht, dass Konstanten nur während der Kompilierung generiert werden, d. h. sie sind nicht voreingestellt Klassendatei. Nur der Inhalt des Pools kann im Methodenbereich in den Pool eingefügt werden. Diese Funktion wird häufig von Entwicklern als intern() des Strings verwendet Klasse-Methode.

Da der Laufzeitkonstantenpool Teil des Methodenbereichs ist, wird er natürlich durch den Speicher des Methodenbereichs begrenzt. Wenn der Konstantenpool keinen Speicher mehr beantragen kann, wird eine OutOfMemoryError-Ausnahme ausgelöst.

Direkter Speicher

Direkter Speicher (Direkter Speicher) ist weder Teil des Laufzeitdatenbereichs der virtuellen Maschine noch ein in der Java Virtual Machine Specification definierter Speicherbereich , aber Dieser Teil des Speichers wird ebenfalls häufig verwendet und kann auch OutOfMemoryError-Ausnahmen verursachen.

Die NIO-Klasse (New Input/Output) wurde zu JDK 1.4 hinzugefügt und eine auf Kanälen und Puffern basierende E/A-Methode eingeführt. Sie kann die native Funktionsbibliothek direkt verwenden, um Off-Heap-Speicher zuzuweisen , und dann über ein DirectByteBuffer-Objekt arbeiten, das im Java-Heap als Referenz auf diesen Speicher gespeichert ist. Dies kann in einigen Szenarien die Leistung erheblich verbessern, da das Hin- und Herkopieren von Daten zwischen dem Java-Heap und dem nativen Heap vermieden wird.

Natürlich wird die Zuteilung des lokalen Direktspeichers nicht durch die Größe des Java-Heaps begrenzt. Da es sich jedoch um Speicher handelt, wird sie definitiv von der Größe des gesamten lokalen Speichers (einschließlich RAM) beeinflusst Swap-Bereich oder Auslagerungsdatei). Wenn Serveradministratoren die Parameter virtueller Maschinen konfigurieren, legen sie im Allgemeinen -Xmx und andere Parameterinformationen basierend auf dem tatsächlichen Speicher fest, ignorieren jedoch häufig den direkten Speicher, sodass die Summe jedes Speicherbereichs größer ist als die physische Speichergrenze (einschließlich physischer und Betriebssystemebenengrenzen). ) ), was während der dynamischen Erweiterung zu einer OutOfMemoryError-Ausnahme führt.

Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in Java-Speicherbereiche und Speicherüberlaufausnahmen. 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