it-swarm-eu.dev

Stapel- und Heapspeicher in Java

Wie ich verstehe, enthält der Stack-Speicher in Java Grundelemente und Methodenaufrufe, und der Heap-Speicher wird zum Speichern von Objekten verwendet.

Angenommen, ich habe eine Klasse

class A {
       int a ;
       String b;
       //getters and setters
}
  1. Wo wird das Grundelement a in der Klasse A gespeichert?

  2. Warum gibt es überhaupt Heapspeicher? Warum können wir nicht alles auf dem Stapel speichern?

  3. Wird der mit dem Objekt verknüpfte Stapel zerstört, wenn für das Objekt Müll gesammelt wird?

101

Der grundlegende Unterschied zwischen Stapel und Heap ist der Lebenszyklus der Werte.

Stapelwerte existieren nur im Rahmen der Funktion, in der sie erstellt wurden. Sobald sie zurückgegeben werden, werden sie verworfen.
Heap-Werte sind jedoch auf dem Heap vorhanden. Sie werden zu einem bestimmten Zeitpunkt erstellt und zu einem anderen Zeitpunkt zerstört (entweder durch GC oder manuell, abhängig von der Sprache/Laufzeit).

Jetzt Java speichert nur Grundelemente auf dem Stapel. Dies hält den Stapel klein und hilft, einzelne Stapelrahmen klein zu halten, wodurch mehr verschachtelte Aufrufe möglich sind.
Objekte werden auf dem Heap erstellt, und nur Referenzen (die wiederum Grundelemente sind) werden auf dem Stapel herumgereicht.

Wenn Sie also ein Objekt erstellen, wird es mit allen dazugehörigen Variablen auf den Heap gelegt, damit es nach der Rückkehr des Funktionsaufrufs bestehen bleibt.

106
back2dos

Wo werden primitive Felder gespeichert?

Primitive Felder werden als Teil des Objekts gespeichert, das irgendwo instanziiert wird . Der einfachste Weg, sich vorzustellen, wo dies ist - ist der Haufen. Dies ist jedoch nicht immer der Fall. Wie beschrieben in Java Theorie und Praxis: Urban Performance Legends, überarbeitet :

JVMs können eine als Escape-Analyse bezeichnete Technik verwenden, mit der sie feststellen können, dass bestimmte Objekte während ihrer gesamten Lebensdauer auf einen einzelnen Thread beschränkt bleiben und dass die Lebensdauer durch die Lebensdauer eines bestimmten Stapelrahmens begrenzt ist. Solche Objekte können sicher auf dem Stapel anstelle des Heaps zugewiesen werden. Noch besser ist, dass die JVM für kleine Objekte die Zuordnung vollständig optimieren und die Felder des Objekts einfach in Register heben kann.

Abgesehen davon, dass "das Objekt erstellt wurde und das Feld auch vorhanden ist", kann man nicht sagen, ob sich etwas auf dem Heap oder auf dem Stapel befindet. Beachten Sie, dass bei kleinen, kurzlebigen Objekten das 'Objekt' möglicherweise nicht als solches im Speicher vorhanden ist und seine Felder stattdessen direkt in Registern abgelegt werden.

Das Papier schließt mit:

JVMs sind überraschend gut darin, Dinge herauszufinden, von denen wir früher angenommen haben, dass nur der Entwickler sie wissen kann. Indem die JVM von Fall zu Fall zwischen Stapelzuweisung und Heapzuweisung wählen kann, können wir die Leistungsvorteile der Stapelzuweisung erzielen, ohne dass sich der Programmierer darüber Gedanken macht, ob er auf dem Stapel oder auf dem Heap zuweisen soll.

Wenn Sie also Code haben, der wie folgt aussieht:

void foo(int arg) {
    Bar qux = new Bar(arg);
    ...
}

wenn der ... nicht zulässt, dass qux diesen Bereich verlässt, kann qux zugewiesen werden stattdessen auf dem Stapel. Dies ist tatsächlich ein Gewinn für das VM, da es bedeutet, dass es niemals Müll gesammelt werden muss - es verschwindet, wenn es den Bereich verlässt.

Mehr zu Fluchtanalyse bei Wikipedia. Für diejenigen, die bereit sind, sich mit Artikeln zu befassen, Escape Analysis für Java von IBM. Für diejenigen, die aus einer C # -Welt kommen, finden Sie möglicherweise Der Stapel ist ein Implementierungsdetail und Die Wahrheit über Werttypen von Eric Lippert gute Lesungen (sie sind nützlich für Java Typen, da viele der Konzepte und Aspekte gleich oder ähnlich sind). Warum sprechen .Net-Bücher über die Zuweisung von Stapel- und Heapspeicher ? geht auch darauf ein.

Auf dem Warum des Stapels und des Haufens

Auf dem Haufen

Warum also überhaupt den Stapel oder den Haufen? Für Dinge, die den Spielraum verlassen, kann der Stapel teuer sein. Betrachten Sie den Code:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

Die Parameter sind ebenfalls Teil des Stapels. In der Situation, dass Sie keinen Heap haben, würden Sie den gesamten Wertesatz auf dem Stapel übergeben. Dies ist in Ordnung für "foo" Und kleine Zeichenfolgen ... aber was würde passieren, wenn jemand eine große XML-Datei in diese Zeichenfolge einfügt. Jeder Aufruf würde die gesamte riesige Zeichenfolge auf den Stapel kopieren - und , dass ziemlich verschwenderisch wäre.

Stattdessen ist es besser, die Objekte, deren Leben außerhalb des unmittelbaren Bereichs liegt (an einen anderen Bereich übergeben, in einer Struktur festgehalten, die von einer anderen Person verwaltet wird usw.), in einen anderen Bereich zu verschieben, der als Heap bezeichnet wird.

Auf dem Stapel

Sie brauchen den Stapel nicht . Man könnte hypothetisch eine Sprache schreiben, die keinen Stapel (beliebiger Tiefe) verwendet. Ein altes BASIC, das ich in meiner Jugend gelernt habe, hat dies getan. Man konnte nur 8 Ebenen von gosub -Aufrufen ausführen und alle Variablen waren global - es gab keinen Stapel.

Der Vorteil des Stapels besteht darin, dass, wenn Sie eine Variable haben, die mit einem Bereich vorhanden ist, dieser Stapelrahmen beim Verlassen dieses Bereichs gelöscht wird. Es vereinfacht wirklich, was da ist und was nicht. Das Programm wechselt zu einer anderen Prozedur, einem neuen Stapelrahmen. Das Programm kehrt zur Prozedur zurück, und Sie befinden sich wieder in der Prozedur, in der Ihr aktueller Bereich angezeigt wird. Das Programm verlässt die Prozedur und alle Elemente auf dem Stapel werden freigegeben.

Dies erleichtert der Person, die die Laufzeit für den Code schreibt, das Leben, um einen Stapel und einen Heap zu verwenden. Sie bieten einfach viele Konzepte und Arbeitsweisen für den Code, die es der Person, die den Code in der Sprache schreibt, ermöglichen, nicht explizit an sie zu denken.

Die Natur des Stapels bedeutet auch, dass er nicht fragmentiert werden kann. Speicherfragmentierung ist ein echtes Problem mit dem Heap. Sie ordnen ein paar Objekte zu, sammeln dann ein mittleres Objekt und versuchen dann, Platz für das nächste große zuzuweisende Objekt zu finden. Es ist ein Chaos. Wenn Sie stattdessen Dinge auf den Stapel legen können, müssen Sie sich nicht darum kümmern.

Wenn etwas Müll gesammelt wird

Wenn etwas Müll gesammelt wird, ist es weg. Es wird jedoch nur Müll gesammelt, weil er bereits vergessen wurde. Es gibt keine Verweise mehr auf das Objekt im Programm, auf das vom aktuellen Status des Programms aus zugegriffen werden kann.

Ich werde darauf hinweisen, dass dies eine sehr große Vereinfachung der Speicherbereinigung ist. Es gibt viele Garbage Collectors (sogar innerhalb von Java - Sie können den Garbage Collector mithilfe verschiedener Flags ( docs ) optimieren. Diese verhalten sich unterschiedlich und die Nuancen, wie jeder die Dinge tut, sind für diese Antwort etwas zu tief. Vielleicht möchten Sie Java Garbage Collection Basics lesen, um eine bessere Vorstellung davon zu bekommen, wie einiges davon funktioniert.

Das heißt, wenn etwas auf dem Stapel zugewiesen ist, wird es nicht als Teil von System.gc() gesammelt - es wird freigegeben, wenn der Stapelrahmen platzt. Wenn sich etwas auf dem Heap befindet und von etwas auf dem Stapel referenziert wird, wird zu diesem Zeitpunkt kein Müll gesammelt.

Warum ist das wichtig?

Zum größten Teil seine Tradition. Die geschriebenen Lehrbücher und Compiler-Klassen sowie die Dokumentation verschiedener Bits machen eine große Sache über den Heap und den Stack.

Die heutigen virtuellen Maschinen (JVM und ähnliche) haben jedoch große Anstrengungen unternommen, um dies vor dem Programmierer zu verbergen. Es spielt keine Rolle, es sei denn, Ihnen geht der eine oder andere aus und Sie müssen wissen , warum (anstatt nur den Speicherplatz angemessen zu vergrößern) viel.

Das Objekt befindet sich irgendwo und befindet sich an der Stelle, an der für die entsprechende Zeit, auf die es existiert, korrekt und schnell zugegriffen werden kann. Wenn es auf dem Stapel oder dem Haufen liegt, spielt es keine Rolle.

49
user40980
  1. Im Heap als Teil des Objekts, auf das durch einen Zeiger im Stapel verwiesen wird. dh. a und b werden nebeneinander gespeichert.
  2. Denn wenn der gesamte Speicher Stapelspeicher wäre, wäre er nicht mehr effizient. Es ist gut, einen kleinen Bereich mit schnellem Zugriff zu haben, in dem wir beginnen, und diese Referenzelemente in dem viel größeren Speicherbereich zu haben, der übrig bleibt. Dies ist jedoch zu viel des Guten, wenn ein Objekt einfach ein einzelnes Grundelement ist, das ungefähr so ​​viel Platz auf dem Stapel einnimmt wie der Zeiger darauf.
  3. Ja.
7
pdr
  1. Auf dem Heap, es sei denn, Java ordnet die Klasseninstanz auf dem Stapel als Optimierung zu, nachdem durch Escape-Analyse nachgewiesen wurde, dass dies keinen Einfluss auf die Semantik hat. Dies ist jedoch ein Implementierungsdetail, also für alle praktischen Zwecke außer Mikrooptimierung lautet die Antwort "auf dem Haufen".

  2. Der Stapelspeicher muss in der ersten Reihenfolge zugewiesen und als letzter freigegeben werden. Der Heapspeicher kann in beliebiger Reihenfolge zugewiesen und freigegeben werden.

  3. Wenn das Objekt durch Müll gesammelt wird, gibt es keine weiteren Verweise vom Stapel darauf. Wenn es so wäre, würden sie das Objekt am Leben erhalten. Stapelprimitive werden überhaupt nicht gesammelt, da sie bei der Rückkehr der Funktion automatisch zerstört werden.

3
dsimcha

Der Stapelspeicher wird zum Speichern lokaler Variablen und des Funktionsaufrufs verwendet.

Während Heap-Speicher zum Speichern von Objekten in Java verwendet wird. Egal, wo das Objekt im Code erstellt wird.

Wo wird das Grundelement a in class A gespeichert werden?

In diesem Fall ist das Grundelement a dem Objekt der Klasse A zugeordnet. So entsteht im Heapspeicher.

Warum gibt es überhaupt Heapspeicher? Warum können wir nicht alles auf dem Stapel speichern?

  • Auf dem Stapel erstellte Variablen werden nicht mehr angezeigt und automatisch zerstört.
  • Der Stapel ist im Vergleich zu Variablen auf dem Heap viel schneller zuzuweisen.
  • Variablen auf dem Heap müssen von Garbage Collector zerstört werden.
  • Langsameres Zuweisen im Vergleich zu Variablen auf dem Stapel.
  • Sie würden den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen und er nicht zu groß ist (primitive lokale Variablen werden im Stapel gespeichert).
  • Sie würden den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie viele Daten zuweisen müssen.

Wird der mit dem Objekt verknüpfte Stapel zerstört, wenn für das Objekt Müll gesammelt wird?

Garbage Collector arbeitet im Bereich des Heap-Speichers und zerstört Objekte, die keine Referenzkette von zu root haben.

3
Premraj