Heim  >  Artikel  >  Java  >  Detaillierte Einführung in Java-Heapspeicher und Stapelspeicher

Detaillierte Einführung in Java-Heapspeicher und Stapelspeicher

高洛峰
高洛峰Original
2017-01-24 14:44:531728Durchsuche

Heap und Stack in Java Java unterteilt den Speicher in zwei Typen: einen ist Stack-Speicher und der andere ist Heap-Speicher.

Einige in der Funktion definierte grundlegende Typvariablen und Objektreferenzvariablen werden im Stapelspeicher der Funktion zugewiesen. Wenn eine Variable in einem Codeblock definiert wird, weist Java Speicherplatz für die Variable auf dem Stapel zu. Wenn der Gültigkeitsbereich der Variablen überschritten wird, gibt Java automatisch den für die Variable zugewiesenen Speicherplatz frei und der Speicherplatz kann sofort freigegeben werden Verwenden Sie es für andere Zwecke.

Heap-Speicher wird zum Speichern von neu erstellten Objekten und Arrays verwendet. Der im Heap zugewiesene Speicher wird vom automatischen Garbage Collector der Java Virtual Machine verwaltet. Nachdem ein Array oder Objekt im Heap generiert wurde, können Sie auch eine spezielle Variable im Stapel definieren. Der Wert dieser Variablen entspricht der ersten Adresse des Arrays oder Objekts im Heap-Speicher Nachdem Sie die Referenzvariable des Arrays oder Objekts erhalten haben, können Sie die Referenzvariable im Stapelspeicher verwenden, um auf das Array oder Objekt im Heap im Programm zuzugreifen. Die Referenzvariable entspricht einem Alias ​​oder Codenamen für das Array oder Objekt .

Referenzvariablen sind gewöhnliche Variablen, die auf dem Stapel zugewiesen werden, wenn Referenzvariablen außerhalb des Gültigkeitsbereichs freigegeben werden, wenn das Programm ausgeführt wird. Das Array und das Objekt selbst werden im Heap zugewiesen. Auch wenn das Programm außerhalb des Codeblocks ausgeführt wird, in dem sich die Anweisung befindet, die das Array und das Objekt selbst generiert, wird der vom Array und Objekt selbst belegte Heap-Speicher nicht freigegeben Auf Arrays und Objekte wird nicht durch Referenzvariablen verwiesen. Wenn sie verwendet werden, werden sie zu Müll und können nicht mehr verwendet werden. Sie belegen jedoch weiterhin Speicher und werden zu einem unbestimmten Zeitpunkt vom Garbage Collector freigegeben. Dies ist auch der Hauptgrund, warum Java mehr Speicher beansprucht. Tatsächlich verweisen Variablen im Stapel auf Variablen im Heap-Speicher, die in Java Zeiger sind!

Heap und Stack in Java

Java unterteilt Speicher in zwei Typen: einen ist Stapelspeicher und der andere ist Heap-Speicher.

1. Stack und Heap sind Orte, die Java zum Speichern von Daten im RAM verwendet. Im Gegensatz zu C++ verwaltet Java den Stapel und den Heap automatisch, und Programmierer können den Stapel oder den Heap nicht direkt festlegen.

 2. Der Vorteil des Stapels besteht darin, dass die Zugriffsgeschwindigkeit schneller ist als die des Heaps, gleich hinter den Registern, die sich direkt in der CPU befinden. Der Nachteil besteht jedoch darin, dass die Größe und Lebensdauer der im Stapel gespeicherten Daten bestimmt werden muss und es an Flexibilität mangelt. Darüber hinaus können Stapeldaten gemeinsam genutzt werden. Der Vorteil des Heaps besteht darin, dass er die Speichergröße dynamisch zuweisen kann und die Lebensdauer dem Compiler nicht im Voraus mitgeteilt werden muss. Der Garbage Collector von Java sammelt automatisch die nicht mehr verwendeten Daten. Der Nachteil besteht jedoch darin, dass die Zugriffsgeschwindigkeit langsam ist, da zur Laufzeit Speicher dynamisch zugewiesen werden muss. ​

  3. In Java gibt es zwei Arten von Datentypen.

Einer davon sind primitive Typen, es gibt insgesamt 8 Typen, nämlich int, short, long, byte, float, double, boolean, char (beachten Sie, dass
keinen grundlegenden String-Typ hat). Diese Art der Definition ist in der Form int a = 3; long b = 255L definiert und wird als automatische Variable bezeichnet. Es ist erwähnenswert, dass automatische Variablen Literalwerte und keine Instanzen von Klassen speichern, das heißt, sie sind keine Referenzen auf Klassen. Hier gibt es keine Klasse. Beispiel: int a = 3; wobei a eine Referenz ist, die auf den Typ int verweist,

auf den Literalwert 3 verweist. Die Daten dieser Literalwerte können bekannt sein, da ihre Größe und Lebensdauer bekannt sind (diese Literalwerte sind in einem bestimmten Programmblock fest definiert und die Feldwerte verschwinden, nachdem der Programmblock beendet wurde
).

Außer Verfolgung Der Grund für die Geschwindigkeit liegt im Stapel.

Darüber hinaus verfügt der Stack über eine sehr wichtige Besonderheit, nämlich die im Stack gespeicherten Daten können gemeinsam genutzt werden. Angenommen, wir definieren gleichzeitig:

int a = 3;
int b = 3;

Der Compiler verarbeitet zuerst int a = 3; wird auf dem Stapel sein Erstellen Sie einen Verweis auf die Variable a in und suchen Sie dann, ob eine Adresse mit dem Literalwert 3 vorhanden ist. Wenn nicht gefunden, öffnen Sie eine Adresse zum Speichern des Literalwerts 3 und zeigen Sie dann auf a an die Adresse 3. Verarbeiten Sie dann int b = 3; nachdem Sie die Referenzvariable von b erstellt haben, wird b direkt auf die Adresse 3 verwiesen, da sich bereits ein Literalwert von 3 auf dem Stapel befindet. Auf diese Weise gibt es eine Situation, in der a und b gleichzeitig auf 3 zeigen.

Es ist wichtig zu beachten, dass sich die Referenz dieses Literalwerts von der Referenz des Klassenobjekts unterscheidet. Gehen Sie davon aus, dass die Referenzen zweier Klassenobjekte gleichzeitig auf dasselbe Objekt verweisen. Wenn eine Objektreferenzvariable den internen Zustand des Objekts ändert, spiegelt die andere Objektreferenzvariable die Änderung sofort wider. Im Gegensatz dazu führt die Änderung des Werts einer Literalreferenz nicht dazu, dass sich auch der Wert einer anderen Referenz auf das Literal ändert. Wie im obigen Beispiel, nachdem wir die Werte von a und b definiert haben, setzen wir dann a=4, dann ist b nicht gleich 4, sondern immer noch gleich 3. Wenn der Compiler auf a=4; trifft, sucht er erneut, ob sich auf dem Stapel ein Literalwert von 4 befindet. Andernfalls wird eine Adresse erneut geöffnet, um den Wert von 4 zu speichern , es wird direkt auf diese Adresse verweisen. Daher haben Änderungen im Wert von a keinen Einfluss auf den Wert von b.

Das andere sind Wrapper-Klassendaten wie Integer, String, Double und andere Klassen, die die entsprechenden Basisdatentypen umschließen. Alle diese Datentypen sind im Heap vorhanden. Java teilt dem Compiler mithilfe der new()-Anweisung explizit mit, dass sie zur Laufzeit dynamisch erstellt werden, sodass sie flexibler sind, aber den Nachteil haben, dass sie mehr Zeit in Anspruch nehmen.

In JAVA gibt es sechs verschiedene Orte, an denen Daten gespeichert werden können:

1. Dies ist der schnellste Speicherbereich, da er sich an einer anderen Stelle als die anderen Speicherbereiche befindet – im Inneren des Prozessors. Da die Anzahl der Register jedoch äußerst begrenzt ist, werden die Register vom Compiler je nach Bedarf zugewiesen. Sie haben keine direkte Kontrolle und können auch keinen Hinweis auf die Existenz des Registers im Programm spüren.

2. Befindet sich im Allzweck-RAM, wird aber vom Prozessor über seinen „Stapelzeiger“ unterstützt. Bewegt sich der Stapelzeiger nach unten, wird neuer Speicher zugewiesen; bewegt er sich nach oben, wird dieser Speicher freigegeben. Dies ist nach Registern eine schnelle und effiziente Methode zur Speicherzuweisung. Beim Erstellen eines Programms muss der JAVA-Compiler die genaue Größe und Lebensdauer aller auf dem Stapel gespeicherten Daten kennen, da er den entsprechenden Code generieren muss, um den Stapelzeiger nach oben und unten zu bewegen. Diese Einschränkung schränkt die Flexibilität des Programms ein. Obwohl einige Java-Daten auf dem Stapel gespeichert werden – insbesondere Objektreferenzen –, werden Java-Objekte dort nicht gespeichert.

3. Ein allgemeiner Speicherpool (existiert auch im RAM), der zum Speichern aller JAVA-Objekte verwendet wird. Der Vorteil, dass sich der Heap vom Stapel unterscheidet, besteht darin, dass der Compiler nicht wissen muss, wie viel Speicherbereich er vom Heap zuweisen muss, und auch nicht wissen muss, wie lange die gespeicherten Daten im Heap verbleiben. Daher besteht eine große Flexibilität bei der Speicherzuweisung auf dem Heap. Wenn Sie ein Objekt erstellen müssen, müssen Sie nur eine einfache Codezeile schreiben. Wenn diese Codezeile ausgeführt wird, wird der Speicher automatisch im Heap zugewiesen. Natürlich muss diese Flexibilität mit entsprechendem Code einhergehen. Die Speicherzuweisung über den Heap nimmt mehr Zeit in Anspruch als die Speicherzuweisung über den Stack.

4. Statische Speicherung. „Statisch“ bedeutet hier „in einer festen Position“. Statischer Speicher speichert Daten, die während der Ausführung des Programms vorhanden sind. Sie können das Schlüsselwort static verwenden, um bestimmte Elemente eines Objekts als statisch zu kennzeichnen, das JAVA-Objekt selbst wird jedoch niemals im statischen Speicherplatz gespeichert.

5. Ständige Lagerung. Konstante Werte werden normalerweise direkt im Programmcode gespeichert, was sicher ist, da sie niemals geändert werden können. Manchmal wird in eingebetteten Systemen die Konstante selbst von anderen Teilen getrennt, daher können Sie sie in diesem Fall im ROM ablegen

6. Wenn die Daten vollständig außerhalb des Programms verbleiben, können sie ohne jegliche Kontrolle des Programms existieren und auch dann existieren, wenn das Programm nicht ausgeführt wird.
In Bezug auf die Geschwindigkeit gibt es die folgende Beziehung:

Register< Heap< Andere

『Der obige Absatz ist ein Auszug aus „Thinking in Java“ 』

Frage eins:

String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true

Frage zwei:

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false

Frage 3:

String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
System.out.println(s3.equals(s4));//true

Einige in der Funktion definierte grundlegende Typvariablen und Objektreferenzvariablen werden im Stapelspeicher der Funktion zugewiesen.

Wenn eine Variable in einem Codeblock definiert wird, weist Java der Variablen Speicherplatz auf dem Stapel zu. Wenn der Gültigkeitsbereich der Variablen überschritten wird, gibt Java automatisch den für die Variable zugewiesenen Speicherplatz frei Speicherplatz kann sofort wiederverwendet werden.

Heap-Speicher wird zum Speichern von neu erstellten Objekten und Arrays verwendet.

Der im Heap zugewiesene Speicher wird vom automatischen Garbage Collector der Java Virtual Machine verwaltet.

Nach dem Generieren eines Arrays oder Objekts im Heap können Sie auch eine spezielle Variable im Stapel definieren, sodass der Wert dieser Variablen im Stapel gleich der ersten Adresse des Arrays oder Objekts ist Im Heap-Speicher wird diese Variable im Stapel zur Referenzvariablen des Arrays oder Objekts.

Eine Referenzvariable entspricht der Benennung eines Arrays oder Objekts. In Zukunft können Sie die Referenzvariable im Stapel verwenden, um auf das Array oder Objekt im Heap im Programm zuzugreifen.
Um genau zu sein: Stack und Heap sind Orte, die Java zum Speichern von Daten im RAM verwendet. Im Gegensatz zu C++ verwaltet Java den Stapel und den Heap automatisch, und Programmierer können den Stapel oder den Heap nicht direkt festlegen.

Javas Heap ist ein Laufzeitdatenbereich, aus dem Klassenobjekte Speicherplatz zuweisen. Diese Objekte werden durch Anweisungen wie new, newarray, anewarray und multianewarray erstellt und erfordern keine explizite Freigabe des Programmcodes. Heap Der Vorteil des Heaps besteht darin, dass er die Speichergröße dynamisch zuweisen kann und die Lebensdauer dem Compiler nicht im Voraus mitgeteilt werden muss, da er den Speicher zur Laufzeit dynamisch zuweist und der Garbage Collector dies automatisch tut Sammeln Sie diese ungenutzten Der Nachteil besteht darin, dass der Speicher zur Laufzeit dynamisch zugewiesen werden muss, sodass die Zugriffsgeschwindigkeit langsam ist.

      栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。  

      栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:  

int a = 3;  
int b = 3;  

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。  

String是一个特殊的包装类数据。可以用:  

String str = new String("abc");
String str = "abc";

   

两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。  
而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。 

          比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。  

String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true

   

可以看出str1和str2是指向同一个对象的。 

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false

   

用new的方式是生成不同的对象。每一次生成一个。  

   因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。  

   另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。(不一定,因为如果事先没有,那么就会创建,这就是创建对象了,如果原来就有,那就指向那个原来的对象就可以了)!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 

更多Java 堆内存与栈内存详细介绍相关文章请关注PHP中文网!

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