Heim  >  Artikel  >  Java  >  Was ist der Unterschied zwischen Heap, Stack und Stack in Java? Einführung in die Unterschiede zwischen Heap, Stack und Stack in Java

Was ist der Unterschied zwischen Heap, Stack und Stack in Java? Einführung in die Unterschiede zwischen Heap, Stack und Stack in Java

不言
不言nach vorne
2018-11-20 15:54:252537Durchsuche

Was Ihnen dieser Artikel bringt, ist der Unterschied zwischen Heap, Stack und Stack in Java? Die Einführung in die Unterschiede zwischen Heap, Stack und Stack in Java hat einen gewissen Referenzwert. Ich hoffe, dass sie für Sie hilfreich ist.

Bevor der formale Inhalt beginnt, möchte ich eines erklären: Der Stack, den wir oft Stack nennen, ist die Sammelbezeichnung für Heap und Stack Stapel. Zusammen werden sie als Stapel bezeichnet. ;

1. Stapel und Heap werden von Java zum Speichern von Daten 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 Stacks 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 Stack-Daten geteilt werden, siehe Punkt 3 für Details. 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. Es gibt zwei Datentypen in Java.

Einer sind primitive Typen, es gibt insgesamt 8 Typen, nämlich int, short, long, byte, float, double, boolean, char (beachten Sie, dass es keine gibt). Grundtyp der Zeichenfolge). 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 zeigt und auf den Literalwert 3 zeigt. Da die Daten dieser Literalwerte in Größe und Lebensdauer bekannt sein können (diese Literalwerte sind in einem bestimmten Programmblock fest definiert und die Feldwerte verschwinden nach dem Beenden des Programmblocks), dient der Geschwindigkeit dies , sie existieren auf dem 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; zuerst erstellt er einen Verweis auf die Variable a auf dem Stapel und findet dann Finden Sie heraus, ob ein Literalwert vorhanden ist. Wenn die Adresse 3 nicht gefunden wird, öffnen Sie eine Adresse, um den Literalwert 3 zu speichern, und zeigen Sie dann a auf die Adresse 3. Verarbeiten Sie dann int b = 3; nachdem Sie die Referenzvariable von b erstellt haben, zeigt b direkt auf die Adresse 3, 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-Daten 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. Dies ist zwar flexibler, hat jedoch den Nachteil, dass es mehr Zeit in Anspruch nimmt.

4. String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建,也可以用String str = "abc";的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java 中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单 例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = "abc";中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。

5. 关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤:

(1)先定义一个名为str的对String类的对象引用变量:String str;

(2)在栈中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并 将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。

(3)将str指向对象o的地址。

值得注意的是,一般String类中字符串值都是直接存值的。但像String str = "abc";这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!

为了更好地说明这个问题,我们可以通过以下的几个代码进行验证。

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

 注意,我们这里并不用str1.equals(str2);的方式,因为这将比较两个字符串的值是否相等。==号,根据JDK的说明,只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是,str1与str2是否都指向了同一个对象。 
结果说明,JVM创建了两个引用str1和str2,但只创建了一个对象,而且两个引用都指向了这个对象。

我们再来更进一步,将以上代码改成:

 String str1 = "abc"; 
 String str2 = "abc"; 
 str1 = "bcd"; 
 System.out.println(str1 + "," + str2); //bcd, abc 
 System.out.println(str1==str2); //false

 这就是说,赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。

事实上,String类被设计成为不可改变(immutable)的类。如果你要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然 后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的,但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中,会带有一定的不良 影响。

再修改原来代码:

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

str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用 str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。

我们再接着看以下的代码。

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

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。

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

创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。 

以上两段代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。

6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改,所有的数据类型包装类都不能更改其内部的值。

7. 结论与建议:

(1)我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,我们创建了String类的对象str。担心陷阱!对象可能并没有被创建!唯一可以肯定的是,指向 String类的引用被创建了。至于这个引用到底是否指向了一个新的对象,必须根据上下文来考虑,除非你通过new()方法来显要地创建一个新的对象。因 此,更为准确的说法是,我们创建了一个指向String类的对象的引用变量str,这个对象引用变量指向了某个值为"abc"的String类。清醒地认 识到这一点对排除程序中难以发现的bug是很有帮助的。

(2)使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是 享元模式的思想,但JDK的内部在这里实现是否应用了这个模式,不得而知。

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

(4)由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量. 
在java中,所有基本类型和引用类型都在栈中存储.栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域).

堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中.

In Java werden alle mit new xxx() erstellten Objekte im Heap gespeichert. Wenn der Garbage Collector erkennt, dass auf ein Objekt nicht verwiesen wird, wird es in Java automatisch zerstört Keine Begrenzung des Wohnraums eines Objekts. Solange es einen Referenztyp gibt, der darauf verweist, kann es überall verwendet werden JAVA, es gibt 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. Stapeln. 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. Haufen. Ein universeller Speicherpool (auch im RAM vorhanden), der zum Speichern aller JAVA-Objekte verwendet wird. Der Vorteil des Unterschieds zwischen Heap und Stack besteht darin, dass der Compiler weder wissen muss, wie viel Speicherbereich er vom Heap zuweisen muss, noch wissen muss, wie lange die gespeicherten Daten im Heap verbleiben. Daher besteht 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. Diese Flexibilität muss natürlich mit entsprechendem Code erkauft werden. 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. Konstante Werte werden normalerweise direkt im Programmcode gespeichert, was sicher ist, da sie niemals geändert werden können. In eingebetteten Systemen sind die Konstanten manchmal selbst von anderen Teilen getrennt. In diesem Fall können Sie sie also im ROM ablegen

6 . Nicht-RAM-Speicher. Wenn die Daten vollständig außerhalb des Programms verbleiben, unterliegen sie keiner Kontrolle durch das Programm und können auch dann vorhanden sein, wenn das Programm nicht ausgeführt wird.
Bezüglich der Geschwindigkeit gilt folgender Zusammenhang:

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

Hier sprechen wir hauptsächlich über die Beziehung zwischen Heap und Stack:

Heap: Heap ist der sogenannte dynamischer Speicher, bei dem der Speicher recycelt werden kann, wenn er nicht benötigt wird, um ihn neuen Speicheranforderungen zuzuweisen. Die Daten im Speicher sind ungeordnet, das heißt, es besteht keine notwendige Positionsbeziehung zwischen dem zuerst zugewiesenen Speicher und dem später zugewiesenen Speicher. Es kann keine Prioritätsreihenfolge geben. Im Allgemeinen wird der Heap vom Benutzer frei zugewiesen und muss manuell freigegeben werden.

Stack: Es ist STACK. Tatsächlich handelt es sich um eine Warteschlange mit nur einem Eingang und Ausgang, nämlich First In Last Out (First In Last Out). Der zuerst zugewiesene Speicher muss später freigegeben werden. Im Allgemeinen wird es vom System automatisch zum Speichern von Funktionsparameterwerten, lokalen Variablen usw. zugewiesen und automatisch gelöscht.

Außerdem ist der Heap global und der Stapel wird beim Eintritt jeder Funktion in kleine Teile geteilt und bei der Rückkehr der Funktion freigegeben. Statische und globale Variablen sowie durch neue erhaltene Variablen werden auf dem Heap platziert, und lokale Variablen werden auf dem Stapel platziert, sodass bei der Rückkehr der Funktion alle lokalen Variablen verschwunden sind.

Tatsächlich wird der Stapel in tatsächlichen Anwendungen hauptsächlich zum Speichern von Methodenaufrufen verwendet. Der Heap wird zur Objektspeicherung verwendet.

Die Grundtypen in JAVA erfordern tatsächlich eine besondere Behandlung. Da in JAVA mit „new“ erstellte Objekte im „Heap“ gespeichert werden, ist die Verwendung von „new“ zum Erstellen einer kleinen, einfachen Variablen, z. B. eines Basistyps usw., häufig nicht sehr effizient. Daher werden in JAVA für diese Typen dieselben Methoden wie in C und C++ verwendet. Mit anderen Worten: Anstatt new zum Erstellen zu verwenden, erstellen Sie eine „automatische“ Variable, die keine „Referenz“ ist. Diese Variable besitzt ihren „Wert“ und wird auf dem Stapel platziert, wodurch sie effizienter wird.

Das obige ist der detaillierte Inhalt vonWas ist der Unterschied zwischen Heap, Stack und Stack in Java? Einführung in die Unterschiede zwischen Heap, Stack und Stack in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:cnblogs.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen