Heim  >  Artikel  >  Java  >  Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

黄舟
黄舟Original
2017-03-27 10:50:501511Durchsuche

In diesem Artikel werden hauptsächlich relevante Informationen für ein umfassendes Verständnis der Java-Speicherzuweisung vorgestellt.

Detaillierte Informationen zur Java-Speicherzuweisung

Dieser Artikel wird von Qian geschrieben. In diesem Artikel werden die Prinzipien der Java-Speicherzuweisung ausführlich vorgestellt, um Anfängern das Erlernen von Java zu erleichtern. Es gibt viele solcher Artikel online, aber die meisten davon sind fragmentiert. Dieser Artikel bietet den Lesern eine systematische Einführung aus der Perspektive kognitiver Prozesse.

Bevor Sie sich mit dem Thema befassen, müssen Sie wissen, dass Java-Programme auf der JVM (Java Virtual Machine, Java Virtual Machine) als Brücke zwischen Java-Programmen und Betriebssystemen verstanden werden können Implementierung der Java-Plattformunabhängigkeit zeigt die Bedeutung von JVM. Wenn Sie die Prinzipien der Java-Speicherzuweisung erlernen, müssen Sie daher bedenken, dass alles in der JVM erfolgt. Die JVM ist die Grundlage und Voraussetzung des Speicherzuweisungsprinzips.

Einfach ausgedrückt umfasst ein vollständiger Java-Programmausführungsprozess die folgenden Speicherbereiche:

l Register: JVM-internes virtuelles Register, Der Zugriff Die Geschwindigkeit ist sehr hoch und das Programm ist unkontrollierbar.

l 🎜>, dh die Referenz (Zeiger) des Heap-Bereichs Objekt . Kann auch zum Speichern von Frames beim Laden von Methoden verwendet werden. l Heap: wird zum Speichern dynamisch generierter Daten verwendet, z. B. neuer Objekte . Beachten Sie, dass die erstellten Objekte nur ihre jeweiligen Mitgliedsvariablen und keine Mitgliedsmethoden enthalten. Da Objekte derselben Klasse ihre eigenen Mitgliedsvariablen haben und in ihren eigenen Heaps gespeichert werden, sie jedoch die Methoden der Klasse gemeinsam nutzen, werden Mitgliedsmethoden nicht jedes Mal kopiert, wenn ein Objekt erstellt wird.

l Konstantenpool: JVM verwaltet einen Konstantenpool für jeden geladenen Typ. Der Konstantenpool ist eine geordnete Sammlung von Konstanten, die von diesem Typ verwendet werden. Einschließlich direkter Konstanten (Basistypen, String) und Symbolverweisen (1) auf andere Typen, Methoden und Felder. Auf Daten im Pool wird wie auf Arrays über einen Index zugegriffen. Da der Konstantenpool alle symbolischen Verweise eines Typs auf andere Typen, Methoden und Felder enthält, spielt der Konstantenpool eine zentrale Rolle bei der dynamischen Verknüpfung von Java.

Der konstante Pool existiert im Heap

. l Codesegment: wird zum Speichern des von der Festplatte gelesenen Quellprogrammcodes verwendet. l

Datensegment:

wird zum Speichern der durch static definierten

statischen

-Mitglieder verwendet. Das Folgende ist das Speicherdarstellungsdiagramm:


Das obige Bild beschreibt grob die Java-Speicherzuweisung und erklärt dann anhand von Beispielen ausführlich, wie Java-Programme im Speicher ausgeführt werden (Hinweis: Die folgenden Bilder stammen aus den J2SE-Kursunterlagen des Lehrers Ma Bingbing aus Shangxuetang. Die rechte Seite des Bildes ist die Programmcode, und die linke Seite ist das Zuordnungsdiagramm, ich werde nacheinander Notizen hinzufügen.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)Vorkenntnisse:

1.

Eine Java-Datei, solange es eine Haupteingabemethode gibt, wir Ich denke, es ist ein Java-Programm, das unabhängig kompiliert und ausgeführt werden kann.

2.Ob es sich um gewöhnliche Typvariablen oder Referenztypvariablen (allgemein als Instanzen bezeichnet) handelt, sie können als lokale Variablen verwendet und auf dem Stapel angezeigt werden. Es ist nur so, dass gewöhnliche Typvariablen ihre entsprechenden Werte direkt auf dem Stapel speichern, während Referenztypvariablen einen Zeiger auf den Heap-Bereich speichern. Über diesen Zeiger können Sie das dieser Instanz entsprechende Objekt im Heap-Bereich finden. Daher belegen gewöhnliche Typvariablen nur einen Teil des Speichers im Stapelbereich, während Referenztypvariablen einen Teil des Speichers im Stapelbereich und im Heap-Bereich belegen.

Beispiel:


Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text) 
1.
Die JVM findet automatisch die Hauptmethode, führt die erste Codezeile aus, erstellt eine Instanz der Testklasse, weist einen Teil des Speichers auf dem Stapel zu und speichert einen Zeiger auf den Heap Objekt 110925.

 2.Erstellen Sie eine Variable vom Typ int Datum Da es sich um einen Basistyp handelt, wird der dem Datum entsprechende Wert 9 direkt im Stapel gespeichert.

  3.Erstellen Sie zwei Instanzen der BirthDate-Klasse, d1 und d2, und speichern Sie entsprechende Zeiger, die auf ihre jeweiligen Objekte im Stapel zeigen. Sie rufen beim Instanziieren den parametrisierten Konstruktor auf, sodass das Objekt benutzerdefinierte Anfangswerte enthält.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

    Rufen Sie die Methode „change1“ des Testobjekts mit dem Datum als Parameter auf. Wenn die JVM diesen Code liest, erkennt sie, dass es sich bei i um eine lokale Variable handelt. Daher legt sie i auf den Stapel und weist i den Wert von date zu.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

   Weisen Sie 1234 i zu. Sehr einfacher Schritt.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

                                                                                                                            

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

                    Die JVM erkennt, dass der Parameter b in der Methode change2 eine lokale Variable ist und fügt ihn sofort zum Stapel hinzu. Da es sich um eine Variable vom Referenztyp handelt, speichert b den Zeiger in d1. Zu diesem Zeitpunkt zeigen b und d1 auf Objekte im gleicher Haufen. Was zwischen b und d1 übergeben wird, ist ein Zeiger.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)
                                                                                                                                                     

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)
Der interne Ausführungsprozess besteht darin, ein neues Objekt im Heap-Bereich zu erstellen und den Zeiger des Objekts an dem Platz zu speichern, der b im Stapel entspricht. Zu diesem Zeitpunkt zeigt Instanz b nicht mehr auf das Objekt, auf das Instanz d1 zeigt. aber das Objekt, auf das die Instanz d1 zeigt Es gibt keine Änderung, daher kann es keine Auswirkungen auf d1 haben.


                                                 Der AP-Speicherplatz muss auf das automatische Recycling warten.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

                    Auf die gleiche Weise weist die JVM Platz für die lokale Referenzvariable b auf dem Stapel zu und speichert den Zeiger in d2 in b. Zu diesem Zeitpunkt zeigen d2 und b auf dasselbe Objekt. Wenn Sie dann die setDay-Methode der Instanz b aufrufen, wird tatsächlich die setDay-Methode des Objekts aufgerufen, auf das d2 zeigt.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

            Der Aufruf der setDay-Methode von Instanz b wirkt sich auf d2 aus, da beide auf dasselbe verweisen ein Objekt.

Vertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text)

Nach Ausführung der change3-Methode wird die lokale Referenzvariable b sofort freigegeben.

Das Obige ist die allgemeine Situation der Speicherzuweisung, wenn ein Java-Programm ausgeführt wird. Tatsächlich ist es nichts ganz Einfaches, wenn man erst einmal die Idee beherrscht. Es gibt lediglich zwei Arten von Variablen: Basistypen und Referenztypen. Beide werden als lokale Variablen auf dem Stapel abgelegt. Der Basistyp speichert den Wert direkt auf dem Stapel. Der Referenztyp speichert nur einen Zeiger auf den Heap-Bereich, und das reale Objekt befindet sich im Heap. Bei Verwendung als Parameter wird der Basistyp direkt als Wert und der Referenztyp als Zeiger übergeben.

Zusammenfassung:

    1.Klar, was eine Instanz und was ein Objekt ist. Klasse a= new Class();Zu diesem Zeitpunkt wird a als Instanz bezeichnet und man kann nicht sagen, dass a ein Objekt ist. Die Instanz befindet sich auf dem Stapel und das Objekt befindet sich auf dem Heap. Durch den Betrieb der Instanz wird das Objekt tatsächlich indirekt über den Instanzzeiger betrieben. Mehrere Instanzen können auf dasselbe Objekt verweisen.

 2.Die Daten im Stapel und die Datenvernichtung im Heap sind nicht synchronisiert. Sobald die Methode beendet ist, werden die lokalen Variablen im Stapel sofort zerstört, die Objekte im Heap werden jedoch nicht unbedingt zerstört. Da möglicherweise andere Variablen auf dieses Objekt verweisen, wird es erst dann zerstört, wenn im Stapel keine Variable mehr auf das Objekt im Heap verweist, und es wird auch nicht sofort zerstört. Es wird erst beim Garbage Collection-Scan zerstört .

 3.Der oben genannte Stapel, Heap, Codesegment, Datensegment usw. beziehen sich alle auf das Anwendungsprogramm. Jede Anwendung entspricht einer eindeutigen JVM-Instanz. Jede JVM-Instanz verfügt über einen eigenen Speicherbereich und beeinflusst sich nicht gegenseitig. Und diese Speicherbereiche werden von allen Threads gemeinsam genutzt. Die hier erwähnten Stapel und Heaps sind Gesamtkonzepte, und diese Stapel können auch unterteilt werden.

 4.Die Mitgliedsvariablen einer Klasse sind in verschiedenen Objekten unterschiedlich und haben ihren eigenen Speicherplatz (Mitgliedsvariablen befinden sich in Objekten im Heap). Die Methoden einer Klasse werden jedoch von allen Objekten der Klasse gemeinsam genutzt. Es gibt nur einen Satz. Die Methoden werden auf den Stapel verschoben. Wenn die Methode nicht verwendet wird, belegt sie keinen Speicher.

Die obige Analyse betrifft nur den Stapel und den Heap, und es gibt auch einen sehr wichtigen Speicherbereich: den konstanten Pool, in dem häufig unerklärliche Probleme auftreten. Der Zweck des Konstantenpools wurde oben erläutert. Es ist nicht erforderlich, ihn genau zu verstehen. Denken Sie jedoch daran, dass er die Konstanten einer geladenen Klasse verwaltet. Als nächstes werden wir die Eigenschaften des konstanten Pools anhand einiger Beispiele veranschaulichen.

Vorkenntnisse:

Grundtypen und Wrapper-Klassen von Grundtypen. Die Grundtypen sind: Byte, Short, Char, Int, Long, Boolean. Die Grundtypen von Verpackungsklassen sind: Byte, Short, Character, Integer, Long und Boolean. Beachten Sie, dass die Groß-/Kleinschreibung beachtet wird. Der Unterschied zwischen den beiden besteht darin, dass der Basistyp im Programm als gewöhnliche Variable wiedergegeben wird und die Wrapper-Klasse des Basistyps eine Klasse ist, die im Programm als Referenzvariable wiedergegeben wird. Daher werden sie an verschiedenen Orten im Speicher gespeichert: Primitive Typen werden auf dem Stapel gespeichert, während primitive Wrapper-Klassen auf dem Heap gespeichert werden. Die oben genannten Wrapper-Klassen implementieren alle die konstante Pooltechnologie, die anderen beiden Wrapper-Klassen vom Typ Gleitkommazahl jedoch nicht. Darüber hinaus implementiert der String-Typ auch die Constant-Pool-Technologie.

Beispiel:

public class test { 
  public static void main(String[] args) {   
    objPoolTest(); 
  } 
 
  public static void objPoolTest() { 
    int i = 40; 
    int i0 = 40; 
    Integer i1 = 40; 
    Integer i2 = 40; 
    Integer i3 = 0; 
    Integer i4 = new Integer(40); 
    Integer i5 = new Integer(40); 
    Integer i6 = new Integer(0); 
    Double d1=1.0; 
    Double d2=1.0; 
     
    System.out.println("i=i0\t" + (i == i0)); 
    System.out.println("i1=i2\t" + (i1 == i2)); 
    System.out.println("i1=i2+i3\t" + (i1 == i2 + i3)); 
    System.out.println("i4=i5\t" + (i4 == i5)); 
    System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));   
    System.out.println("d1=d2\t" + (d1==d2));  
     
    System.out.println();     
  } 
}

Ergebnis:

i=i0  true 
i1=i2  true 
i1=i2+i3    true 
i4=i5  false 
i4=i5+i6    true 
d1=d2  false

ErgebnisAnalyse

1.i und i0 sind beide Variablen vom Typ „int“ (gemeinsam), daher werden die Daten direkt im Stapel gespeichert, und der Stapel verfügt über eine sehr wichtige Funktion: Daten im Stapel können geteilt werden. Wenn wir int i = 40; und dann int i0 = 40; definieren, prüfen wir automatisch, ob sich 40 im Stapel befinden. Wenn ja, zeigt i0 direkt auf i's 40 und es werden keine neuen 40 hinzugefügt.

2.i1 und i2 sind beide Referenztypen und speichern Zeiger auf dem Stapel, da Integer eine Wrapper-Klasse ist. Da die Integer-Verpackungsklasse die Konstantenpool-Technologie implementiert, werden die 40 von i1 und i2 beide aus dem Konstantenpool abgerufen und zeigen auf dieselbe Adresse, also ist i1 = 12.

3.Offensichtlich ist dies eine Additionsoperation. Die mathematischen Operationen von Java werden alle auf dem Stapel ausgeführt., Java berechnet automatisch i1 und i2. Die Unboxing-Operation wird ausgeführt und in eine Ganzzahl konvertiert, sodass i1 numerisch gleich i2+i3 ist.

4. i4 und i5 sind beide Referenztypen und speichern Zeiger auf dem Stapel, da Integer eine Wrapper-Klasse ist. Da sie jedoch jeweils neu sind, suchen sie nicht mehr nach Daten aus dem Konstantenpool, sondern nach einem neuen Objekt aus dem Heap und speichern dann jeweils den Zeiger auf das Objekt, sodass i4 und i5 nicht gleich sind, da sie unterschiedliche Zeiger speichern. Die spitzen Gegenstände sind unterschiedlich.

5.Dies ist ebenfalls eine Additionsoperation, genau wie 3.

6.d1 und d2 sind beide Referenztypen und speichern Zeiger auf dem Stapel, da Double eine Wrapper-Klasse ist. Die Double-Verpackungsklasse implementiert jedoch keine konstante Pooltechnologie, daher entspricht Doubled1=1.0; Double d1=new Double(1.0);, einem neuen Objekt aus dem Heap, und dasselbe gilt für d2. Daher sind die in d1 und d2 gespeicherten Zeiger unterschiedlich und zeigen auf unterschiedliche Objekte, sodass sie nicht gleich sind.

Zusammenfassung:

1.Die oben genannten Grundtypen der Verpackungsklassen implementieren alle die konstante Pooltechnologie, aber die von ihnen verwalteten Konstanten sind nur Konstanten im Bereich [-128 bis 127]. , Objekte werden aus dem Heap erstellt und nicht mehr aus dem Konstantenpool entnommen. Wenn das obige Beispiel beispielsweise in Integer i1 = 400; Integer i2 = 400; geändert wird, ist es offensichtlich, dass es 127 überschreitet und die Konstante nicht aus dem Konstantenpool abgerufen werden kann, sodass ein neues Integer-Objekt aus erstellt werden muss Heap. Zu diesem Zeitpunkt sind i1 und i2 nicht gleich.

2. Der String-Typ implementiert ebenfalls die Constant-Pool-Technologie, unterscheidet sich jedoch geringfügig. Der String-Typ erkennt zunächst, ob ein entsprechender -String im Konstantenpool vorhanden ist. Wenn nicht, wird dieser entfernt.

Fußnote:

(1) Symbolreferenz ist, wie der Name schon sagt, ein Symbol. Die Symbolreferenz lautet: Dieses Symbol wird nur analysiert, wenn es verwendet wird. Wenn Sie mit dem Linux- oder Unix-System vertraut sind, können Sie sich diesen Symbolverweis als Softlink zu einer Datei vorstellen. Wenn Sie diesen Softlink verwenden, wird er tatsächlich analysiert und erweitert, um die eigentliche Datei zu finden

Für Symbolreferenzen gibt es mehr Diskussionen auf der Klassenladeebene, und auf der Quellcodeebene handelt es sich lediglich um eine formelle Diskussion.

Wenn eine Klasse geladen wird, werden die symbolischen Referenzen anderer von der Klasse verwendeter Klassen im Konstantenpool gespeichert. Wenn der eigentliche Code ausgeführt wird, speichert die JVM den Konstantenpool, wenn sie auf eine andere Klasse trifft Beim ersten Mal wird die symbolische Referenz der Klasse erweitert und in eine direkte Referenz umgewandelt, sodass die JVM sie beim nächsten Auftreten desselben Typs nicht mehr analysiert, sondern diese direkt analysierte Referenz direkt verwendet.

Zusätzlich zu der oben genannten symbolischen Referenzanweisung im Klassenladeprozess basiert sie auf Quellcodeebene auf dem Referenzanalyseprozess, um zu unterscheiden, ob es sich bei bestimmten Daten im Code um eine symbolische Referenz oder eine direkte Referenz handelt , wie zum Beispiel System. out.println("test" + "abc");//Der hier auftretende Effekt entspricht einem direkten Verweis und setzt einen bestimmten Strings = "abc" voraus. ; System.out. println("test" + s);//Der Effekt entspricht einer symbolischen Referenz, das heißt, s wird erweitert und analysiert, was dem Sein von s entspricht eine symbolische Verknüpfung von „abc“, also im kompilierten Zustand. Zu diesem Zeitpunkt erweitert die Klassendatei s nicht direkt, sondern behandelt dieses s als Symbol, das erweitert wird, wenn der eigentliche Code ausgeführt wird.

Das obige ist der detaillierte Inhalt vonVertieftes Verständnis der Java-Speicherzuweisung (Bilder und Text). 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