So werden Java Objekte auf dem Heap gespeichert und referenziert
Java hat eine interne Speicherverwaltung.
Eigentlich ist es nicht nur eine. Es sind mehrere.
Uns interessieren nur zwei.
Methoden und lokale Variablen werden auf dem Stack verwaltet.
Und die Speicherverwaltung für Java Objekte und den Instanzvariablen findet auf dem Heap statt.
Ja wieso braucht Java eigentlich zwei Speichersysteme?
Es ist ganz einfach.
Java Objekte werden an einem anderen Ort, gespeichert als Methoden oder lokale Variablen.
Denn ein Objekt umfasst meistens mehrere Variable.
Es ist somit ein ganz anderer Speicherbedarf notwendig.
Außerdem muss ein Objekt, unter Umständen, auch länger im Programm gehalten werden.
Okay und warum solltest du das wissen?
Stell dir vor, du schreibst ein Java Spiel.
Irgendeins bei dem du irgendwelche Vögel auf dem Bildschirm abschießen musst.
Jeder Vogel, welcher durch den Spieler getroffen wurde, verschwindet vom Bildschirm.
Und dafür kommt ein neuer Vogel.
Was meinst du?
Wenn der Vogel vom Bildschirm verschwindet, verschwindet er dann auch aus dem Speicher?
Kann es sein, dass sich der Speicher nach und nach füllt mit Vogel-Objekten, welche niemand braucht.
Und um diese Dinge zu vermeiden, solltest du wissen, wie Java Objekte speicherintern verwaltet werden.
Inhalt
- 1 Java Objekte existieren auf dem Heap
- 2 Wenn du Java Objekte aus dem Heap entfernen willst, schenke ihnen einfach keine Beachtung mehr.
- 3 Für Java Objekte ohne Verweis ist der Garbage Collector zuständig.
- 4 Instanzvariablen existieren solange, wie die dazu gehörigen Java Objekte.
- 5 Eine Referenzvariable kann auch auf verschiedene Objekte verweisen.
- 6 Zusammenfassung:
Java Objekte existieren auf dem Heap
In einem anderen Beitrag habe ich schon einmal geschrieben, dass Methoden und lokale Variablen auf dem Stack gestapelt werden.
Für Java Objekte existiert eine andere Speicherverwaltung.
Und diese nennt sich Heap.
Bleiben wir beim Vogelspiel.
Im nachfolgenden Java Code habe ich, innerhalb der main Methode, ein „Vogelobjekt“ angelegt.
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel();
}
}
Wir haben bereits geklärt, dass die main Methode auf dem Stack liegt.
Und lokale Variablen liegen ebenfalls auf dem Stack.
Aber in diesem Fall existiert noch ein Objekt.
Was ist mit diesem?
Ganz einfach.
Die Referenzvariable ist samt Main-Methode auf dem Stack.
Und diese verweist schließlich nur auf das Objekt.
Das eigentliche Objekt liegt auf dem Heap.
Und nun?
Auf dem Heap wird dieses Objekt solange bereitgestellt, bis es nicht mehr gebraucht oder zerstört wird.
Wenn du Java Objekte aus dem Heap entfernen willst, schenke ihnen einfach keine Beachtung mehr.
Hört sich blöd an – Aber so ist es.
Im Vogelspiel wird es doch so sein:
- Erster Vogel taucht auf.
- Dieser wird abgeschossen.
- Und dann kommt der nächste Vogel ins Spiel.
Das heißt.
Ich lege einen zweites „Vogelobjekt“ und eine zweite Referenzvariable an.
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel(); //Erstes Vogelobjekt
VogelSpiel vogelZwei = new VogelSpiel(); //Zweites Objekt
}
}
- Die Referenzvariable „vogelEins“ verweist auf das erste Objekt.
- Die Referenzvariable „vogelZwei“ verweist auf das zweite Objekt.
Wenn der erste Vogel abgeschossen wurde, brauch ich diesen doch nicht mehr.
Und jetzt kann ich den Zeiger bzw. den Verweis auf das erste Objekt entfernen.
Dazu muss ich der Variablen „vogelEins“ den Wert der Variablen „vogelZwei“ zuweisen.
Zur Errinnerung:
Referenzvariablen speichern keine Zahl oder Ähnliches.
Sie speichern lediglich den Verweis auf ein Java Objekt.
Und wenn du der Variablen „vogelEins“ jetzt den Wert aus „vogelZwei“ zuweist, dann switchst du den Verweis um.
Und Objekt 1 steht nun ohne Referenzvariable da.
Stattdessen verweisen jetzt zwei Variablen auf das zweite Objekt.
Und so würde das Ganze im Java Code aussehen:
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel(); //Erstes Vogelobjekt
VogelSpiel vogelZwei = new VogelSpiel(); //Zweites Objekt
vogelEins=vogelZwei; //Wert der Variablen vogelZwei wird vogelEins zugewiesen
}
}
Okay, was passiert nun mit dem ersten Objekt?
Für Java Objekte ohne Verweis ist der Garbage Collector zuständig.
Und der Garbage Collector ist die Müllabfuhr von Java.
Dieses Feature hat nur eine Aufgabe.
Es sucht im ganzen Programm nach Objekten ohne Verweis und löscht diese aus dem Speicher.
Es würde somit das Objekt 1 vom Heap löschen und somit wieder Speicher freigeben.
Wie aktivierst du nun die Garbage Collection?
Gar nicht.
Das ganze passiert automatisch.
Der Garbage Collector erkennt automatisch:
- „Ah, Objekt 1 vom Typ „VogelSpiel“ hat keinen Verweis.
- Also schick ich das Ding ab in den Müll.
- Und gebe wertvollen Speicher für neue Objekte frei.“
Cool, oder?
Aber was passiert mit den ganzen Instanzvariablen?
Instanzvariablen existieren solange, wie die dazu gehörigen Java Objekte.
Ich setze alles noch einmal auf Anfang.
Wir haben zwei Vogelobjekte, mit Referenzvariablen „vogelEins“ und „vogelZwei“.
VogelEins hat eine Größe von 22 Pixel.
VogelZwei hat eine Größe von 17 Pixel.
Somit hat jedes Objekt seine eigene individuelle Instanzvariable.
Und so sieht der Code dazu aus.
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel(); //Erstes Vogelobjekt
VogelSpiel vogelZwei = new VogelSpiel(); //Zweites Objekt
vogelEins.vogelGroesse=22; // Vogel Eins mit 22px
vogelZwei.vogelGroesse=17; // Vogel zwei mit 17 px
}
}
Wenn ich jetzt den Zeiger von „vogelEins“ auf „vogelZwei“ switche, dann übertragen sich auch alle Werte der Instanzvariablen.
Aus Kontrollzwecken habe ich ein paar Bildschirmanzeigen in den Java Code einfließen lassen.
Klicke einmal „Run“ und du siehst das Ergebnis.
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel(); //Erstes Vogelobjekt
System.out.println("VogelEins wurde angelegt");
VogelSpiel vogelZwei = new VogelSpiel(); //Zweites Objekt
System.out.println("VogelZwei wurde angelegt");
vogelEins.vogelGroesse=22; // Vogel Eins mit 22px
System.out.println("VogelEins hat eine Größe von: "+ vogelEins.vogelGroesse);
vogelZwei.vogelGroesse=17; // Vogel zwei mit 17 px
System.out.println("VogelZwei hat eine Größe von: "+ vogelZwei.vogelGroesse);
vogelEins=vogelZwei;
System.out.println("VogelEins wurde auf VogelZwei umgeswicht");
System.out.println("Jetzt hat VogelEins die Größe: "+vogelEins.vogelGroesse);
}
}
Und wenn die Java Objekte ohne Verweis zerstört werden.
Dann zerstört der Garbage Collector auch die dazu gehörigen Instanzvariablen.
Denn diese werden durch das Um-switchen ebenfalls überflüssig.
So und wenn du jetzt so ein Vogelspiel anlegst und dann circa 1000 Vogelobjekte durch den Bildschirm fliegen.
Und du durch eine Methode immer schön die Verweise abänderst, dann hältst du immer nur ein Objekt im Spiel.
Alle anderen Objekte werden durch den fehlenden Verweis zerstört.
Okay, du hast zwar nur ein Objekt.
Dennoch existieren irgendwann 1.000 Referenzvariablen.
Ist doch auch blöd, oder?
Lass uns das noch ändern.
Eine Referenzvariable kann auch auf verschiedene Objekte verweisen.
Ich stell mir das Ganze so vor:
- Erstes Vogelobjekt wird mit einer Referenzvariablen angelegt.
- Sobald das erste Objekt nicht mehr gebraucht wird, wird ein zweites Objekt angelegt.
Jetzt wird aber keine zweite Referenzvariable angelegt. - Stattdessen verweist die ursprüngliche Variable vom ersten Java Objekt auf das zweite.
Somit wird das erste Objekt unbrauchbar und es ist Futter für den Garbage Collector.
Besser, oder?
Und so geht’s.
Und zwar legst du immer wieder neue Objekte an.
Du legst aber keine neuen Referenzvariablen für diese Java Objekte an.
public class VogelSpiel {
int vogelGroesse; // Größe in Pixel
public static void main(String[] args) {
VogelSpiel vogelEins = new VogelSpiel(); //Erstes Vogelobjekt
vogelEins.vogelGroesse=22;
System.out.println("Neuer Vogel mit "+vogelEins.vogelGroesse+" im Spiel.");
System.out.println("Vogel tot");
vogelEins=new VogelSpiel(); //Zweites Vogelobjekt ohne neue Referenzvariable
vogelEins.vogelGroesse=17;
System.out.println("Neuer Vogel mit "+vogelEins.vogelGroesse+" im Spiel.");
System.out.println("Vogel tot");
vogelEins=new VogelSpiel(); //Drittes Vogelobjekt ohne neue Referenzvariable
vogelEins.vogelGroesse=5;
System.out.println("Neuer Vogel mit "+vogelEins.vogelGroesse+" im Spiel.");
System.out.println("Vogel tot");
}
}
Somit brauchst du eine Variable, welche auf das aktuelle Objekt zeigt.
Alle Objekte, welche unbrauchbar sind, besitzen keinen Verweis.
Und diese werden vom Heap geschafft und es steht immer neuer Speicher zur Verfügung.
Zusammenfassung:
- Java unterscheidet zwei wesentliche Speichersysteme.
- Java Objekte werden im sogenannten Heap gespeichert.
Instanzvariablen gehören zu den Objekten und werden ebenfalls dort verwaltet. - Methoden und deren lokalen Variablen befinden sich im Stack-Speicher.
- Wenn du in der main-Methode ein Objekt anlegst, befindet sich die Referenzvariable im Stack und das Objekt im Heap.
- Um den Heap immer wieder zu bereinigen, musst du nur die entsprechende Referenzvariable um-switchen.
Alle Java Objekte ohne Verweis werden durch den Garbage Collector automatisch aus dem Heap entfernt.