Speicherverwaltung in Java Interview Fragen (+ Antworten)

Dieser Artikel ist Teil einer Reihe: • Fragen zum Interview mit Java Collections

• Fragen zum Java Type System-Interview

• Fragen zum Java Concurrency-Interview (+ Antworten)

• Fragen zum Java-Klassenstruktur- und Initialisierungsinterview

• Fragen zum Java 8-Interview (+ Antworten)

• Speicherverwaltung in Java-Interviewfragen (+ Antworten) (aktueller Artikel) • Java Generics-Interviewfragen (+ Antworten)

• Fragen zum Java Flow Control-Interview (+ Antworten)

• Fragen zum Interview mit Java-Ausnahmen (+ Antworten)

• Fragen zum Interview mit Java Annotations (+ Antworten)

• Fragen zum Top Spring Framework-Interview

1. Einleitung

In diesem Artikel werden einige Fragen zur Speicherverwaltung behandelt, die bei Java-Entwicklerinterviews häufig auftauchen. Speicherverwaltung ist ein Bereich, mit dem nicht so viele Entwickler vertraut sind.

Tatsächlich müssen sich Entwickler im Allgemeinen nicht direkt mit diesem Konzept befassen, da sich die JVM um die Details kümmert. Sofern nichts ernsthaft schief geht, haben selbst erfahrene Entwickler möglicherweise keine genauen Informationen zur Speicherverwaltung zur Hand.

Auf der anderen Seite sind diese Konzepte in Interviews weit verbreitet - also lasst uns gleich loslegen.

2. Fragen

Q1. Was bedeutet die Anweisung "Speicher wird in Java verwaltet"?

Speicher ist die Schlüsselressource, die eine Anwendung benötigt, um effektiv ausgeführt zu werden, und wie jede Ressource ist er knapp. Daher erfordert die Zuordnung und Freigabe zu und von Anwendungen oder verschiedenen Teilen einer Anwendung viel Sorgfalt und Überlegung.

In Java muss ein Entwickler jedoch nicht explizit Speicher zuweisen und freigeben - die JVM und insbesondere den Garbage Collector - hat die Aufgabe, die Speicherzuweisung zu handhaben, damit der Entwickler dies nicht tun muss.

Dies steht im Gegensatz zu dem, was in Sprachen wie C passiert, in denen ein Programmierer direkten Zugriff auf den Speicher hat und buchstäblich auf Speicherzellen in seinem Code verweist, wodurch viel Platz für Speicherlecks geschaffen wird.

Q2. Was ist Müllabfuhr und was sind ihre Vorteile?

Bei der Speicherbereinigung wird der Heapspeicher überprüft, ermittelt, welche Objekte verwendet werden und welche nicht, und die nicht verwendeten Objekte werden gelöscht.

Ein verwendetes Objekt oder ein referenziertes Objekt bedeutet, dass ein Teil Ihres Programms weiterhin einen Zeiger auf dieses Objekt verwaltet. Ein nicht verwendetes Objekt oder ein nicht referenziertes Objekt wird von keinem Teil Ihres Programms mehr referenziert. So kann der von einem nicht referenzierten Objekt verwendete Speicher zurückgefordert werden.

Der größte Vorteil der Speicherbereinigung besteht darin, dass wir die manuelle Speicherzuweisung / Freigabe nicht mehr benötigen, damit wir uns auf die Lösung des vorliegenden Problems konzentrieren können.

Q3. Gibt es Nachteile der Speicherbereinigung?

Ja. Immer wenn der Garbage Collector ausgeführt wird, wirkt sich dies auf die Leistung der Anwendung aus. Dies liegt daran, dass alle anderen Threads in der Anwendung gestoppt werden müssen, damit der Garbage Collector-Thread seine Arbeit effektiv ausführen kann.

Abhängig von den Anforderungen der Anwendung kann dies ein echtes Problem sein, das vom Client nicht akzeptiert wird. Dieses Problem kann jedoch durch geschickte Optimierung und Optimierung des Garbage Collectors und Verwendung verschiedener GC-Algorithmen erheblich reduziert oder sogar beseitigt werden.

Q4. Was bedeutet der Begriff „Stop-The-World“?

Wenn der Garbage Collector-Thread ausgeführt wird, werden andere Threads gestoppt, was bedeutet, dass die Anwendung vorübergehend gestoppt wird. Dies ist analog zur Hausreinigung oder Begasung, bei der den Insassen der Zugang verweigert wird, bis der Vorgang abgeschlossen ist.

Abhängig von den Anforderungen einer Anwendung kann die Speicherbereinigung "Stop the World" zu einem inakzeptablen Einfrieren führen. Aus diesem Grund ist es wichtig, Garbage Collector-Tuning und JVM-Optimierung durchzuführen, damit das festgestellte Einfrieren zumindest akzeptabel ist.

Q5. Was sind Stapel und Haufen? Was ist in jeder dieser Speicherstrukturen gespeichert und wie hängen sie zusammen?

Der Stapel ist ein Teil des Speichers, der Informationen über verschachtelte Methodenaufrufe bis zur aktuellen Position im Programm enthält. Es enthält auch alle lokalen Variablen und Verweise auf Objekte auf dem Heap, die in aktuell ausgeführten Methoden definiert sind.

Diese Struktur ermöglicht es der Laufzeit, von der Methode zurückzukehren und die Adresse zu kennen, von der sie aufgerufen wurde, und alle lokalen Variablen nach dem Beenden der Methode zu löschen. Jeder Thread hat seinen eigenen Stapel.

Der Heap ist ein großer Teil des Speichers, der für die Zuweisung von Objekten vorgesehen ist. Wenn Sie ein Objekt mit dem neuen Schlüsselwort erstellen , wird es auf dem Heap zugewiesen. Der Verweis auf dieses Objekt befindet sich jedoch auf dem Stapel.

Q6. Was ist die Garbage Collection der Generation und was macht sie zu einem beliebten Ansatz der Garbage Collection?

Generations-Garbage-Collection kann lose als die Strategie definiert werden, die vom Garbage-Collector verwendet wird, bei der der Heap in eine Reihe von Abschnitten unterteilt wird, die als Generationen bezeichnet werden und in denen Objekte Objekte entsprechend ihrem „Alter“ auf dem Heap enthalten.

Immer wenn der Garbage Collector ausgeführt wird, wird der erste Schritt des Prozesses als Markierung bezeichnet. Hier identifiziert der Garbage Collector, welche Speicherelemente verwendet werden und welche nicht. Dies kann sehr zeitaufwändig sein, wenn alle Objekte in einem System gescannt werden müssen.

Je mehr Objekte zugewiesen werden, desto größer wird die Liste der Objekte, was zu einer immer längeren Speicherbereinigungszeit führt. Die empirische Analyse von Anwendungen hat jedoch gezeigt, dass die meisten Objekte nur von kurzer Dauer sind.

Bei der generellen Speicherbereinigung werden Objekte nach ihrem „Alter“ in Bezug auf die Anzahl der überlebten Speicherbereinigungszyklen gruppiert. Auf diese Weise verteilte sich der Großteil der Arbeit auf verschiedene kleine und große Sammelzyklen.

Heute sind fast alle Müllsammler Generationen. Diese Strategie ist so beliebt, weil sie sich im Laufe der Zeit als optimale Lösung erwiesen hat.

Q7. Beschreiben Sie ausführlich, wie die Garbage Collection für Generationen funktioniert

Um zu verstehen, wie die Speicherbereinigung von Generationen funktioniert, ist es wichtig, sich zunächst daran zu erinnern, wie der Java-Heap strukturiert ist, um die Speicherbereinigung von Generationen zu erleichtern.

Der Haufen ist in kleinere Räume oder Generationen unterteilt. Diese Räume sind Young Generation, Old oder Tenured Generation und Permanent Generation.

Die junge Generation beherbergt die meisten neu geschaffenen Objekte . Eine empirische Untersuchung der meisten Anwendungen zeigt, dass die meisten Objekte schnell von kurzer Dauer sind und daher bald für die Sammlung in Frage kommen. Daher beginnen hier neue Objekte ihre Reise und werden erst nach Erreichen eines bestimmten „Alters“ in den Raum der alten Generation „befördert“.

Der Begriff „Alter“ bei der Speicherbereinigung von Generationen bezieht sich auf die Anzahl der Erfassungszyklen, die das Objekt überlebt hat .

Der Raum der jungen Generation ist weiter in drei Räume unterteilt: einen Eden-Raum und zwei Überlebensräume wie Survivor 1 (s1) und Survivor 2 (s2).

Die alte Generation beherbergt Objekte, die länger als ein bestimmtes „Alter“ im Gedächtnis geblieben sind . Die Objekte, die die Müllabfuhr der jungen Generation überlebt haben, werden in diesen Raum befördert. Es ist in der Regel größer als die junge Generation. Da die Speicherbereinigung größer ist, ist sie teurer und tritt seltener auf als in der jungen Generation.

Die permanente Generierung oder häufiger PermGen enthält Metadaten, die von der JVM zur Beschreibung der in der Anwendung verwendeten Klassen und Methoden benötigt werden. Es enthält auch den Zeichenfolgenpool zum Speichern internierter Zeichenfolgen. Es wird zur Laufzeit von der JVM basierend auf den von der Anwendung verwendeten Klassen ausgefüllt. Außerdem können hier Plattformbibliotheksklassen und -methoden gespeichert werden.

Zunächst werden alle neuen Objekte dem Eden-Raum zugewiesen . Beide Überlebensfelder beginnen leer. Wenn der Eden-Raum voll ist, wird eine kleine Speicherbereinigung ausgelöst. Referenzierte Objekte werden in den ersten Überlebensraum verschoben. Nicht referenzierte Objekte werden gelöscht.

Während des nächsten kleinen GC passiert dasselbe mit dem Eden-Raum. Nicht referenzierte Objekte werden gelöscht und referenzierte Objekte werden in einen Überlebensbereich verschoben. In diesem Fall werden sie jedoch in den zweiten Überlebensraum (S2) verschoben.

Außerdem wird das Alter von Objekten aus dem letzten kleinen GC im ersten Überlebensraum (S1) erhöht und nach S2 verschoben. Sobald alle überlebenden Objekte nach S2 verschoben wurden, werden sowohl der S1- als auch der Eden-Raum gelöscht. Zu diesem Zeitpunkt enthält S2 Objekte mit unterschiedlichem Alter.

Bei der nächsten kleinen GC wird der gleiche Vorgang wiederholt. Diesmal wechseln jedoch die Überlebensräume. Referenzierte Objekte werden sowohl von Eden als auch von S2 nach S1 verschoben. Überlebende Objekte sind gealtert. Eden und S2 werden gelöscht.

Nach jedem kleinen Speicherbereinigungszyklus wird das Alter jedes Objekts überprüft. Diejenigen, die ein bestimmtes willkürliches Alter erreicht haben, beispielsweise 8, werden von der jungen Generation zur alten oder festen Generation befördert. Für alle nachfolgenden kleineren GC-Zyklen werden Objekte weiterhin in den Raum der alten Generation befördert.

Dies erschöpft den Prozess der Müllabfuhr in der jungen Generation ziemlich. Schließlich wird eine große Müllabfuhr für die alte Generation durchgeführt, die diesen Raum aufräumt und verdichtet. Für jeden Haupt-GC gibt es mehrere Neben-GCs.

Q8. Wann ist ein Objekt für die Speicherbereinigung berechtigt? Beschreiben Sie, wie der Gc ein berechtigtes Objekt sammelt.

Ein Objekt kann für die Garbage Collection oder GC verwendet werden, wenn es nicht über Live-Threads oder statische Verweise erreichbar ist.

Der einfachste Fall, dass ein Objekt für die Speicherbereinigung in Frage kommt, ist, wenn alle seine Referenzen null sind. Zyklische Abhängigkeiten ohne externe externe Referenz sind ebenfalls für die GC berechtigt. Wenn also Objekt A auf Objekt B und Objekt B auf Objekt A verweist und sie keine andere Live-Referenz haben, können sowohl Objekt A als auch B für die Garbage Collection verwendet werden.

Ein weiterer offensichtlicher Fall ist, wenn ein übergeordnetes Objekt auf null gesetzt wird. Wenn ein Küchenobjekt intern auf ein Kühlschrankobjekt und ein Spülenobjekt verweist und das Küchenobjekt auf Null gesetzt ist, können sowohl der Kühlschrank als auch die Spüle zusammen mit der übergeordneten Küche Müll sammeln.

Q9. Wie löst man die Garbage Collection aus Java Code aus?

Sie als Java-Programmierer können die Speicherbereinigung in Java nicht erzwingen . Es wird nur ausgelöst, wenn JVM der Meinung ist, dass eine Garbage Collection basierend auf der Größe des Java-Heapspeichers erforderlich ist.

Vor dem Entfernen eines Objekts aus dem Speicher ruft der Garbage Collection-Thread die Methode finalize () dieses Objekts auf und bietet die Möglichkeit, alle erforderlichen Bereinigungen durchzuführen. Sie können diese Methode auch für einen Objektcode aufrufen. Es gibt jedoch keine Garantie dafür, dass beim Aufrufen dieser Methode eine Speicherbereinigung erfolgt.

Darüber hinaus gibt es Methoden wie System.gc () und Runtime.gc (), mit denen die Anforderung der Garbage Collection an JVM gesendet wird. Es kann jedoch nicht garantiert werden, dass die Garbage Collection erfolgt.

Q10. Was passiert, wenn nicht genügend Speicherplatz für die Speicherung neuer Objekte vorhanden ist?

Wenn in Heap kein Speicherplatz zum Erstellen eines neuen Objekts vorhanden ist, löst Java Virtual Machine OutOfMemoryError oder genauer gesagt java.lang.OutOfMemoryError- Heapspeicher aus .

Q11. Ist es möglich, ein Objekt, das für die Speicherbereinigung in Frage kam, wiederzubeleben?

Wenn ein Objekt für die Speicherbereinigung in Frage kommt, muss der GC die Finalize- Methode ausführen . Die Finalisierungsmethode wird garantiert nur einmal ausgeführt, daher kennzeichnet der GC das Objekt als finalisiert und gibt ihm eine Pause bis zum nächsten Zyklus.

In der Finalize- Methode können Sie ein Objekt technisch „wiederbeleben“, indem Sie es beispielsweise einem statischen Feld zuweisen . Das Objekt würde wieder lebendig und für die Speicherbereinigung nicht mehr geeignet sein, sodass der GC es im nächsten Zyklus nicht erfassen würde.

Das Objekt wird jedoch als finalisiert markiert. Wenn es also wieder förderfähig wird, wird die Finalisierungsmethode nicht aufgerufen. Im Wesentlichen können Sie diesen Trick der „Auferstehung“ für die Lebensdauer des Objekts nur einmal ausführen. Beachten Sie, dass dieser hässliche Hack nur verwendet werden sollte, wenn Sie wirklich wissen, was Sie tun. Wenn Sie diesen Trick jedoch verstehen, erhalten Sie einen Einblick in die Funktionsweise des GC.

Q12. Beschreiben Sie starke, schwache, weiche und Phantomreferenzen und ihre Rolle bei der Speicherbereinigung.

Ähnlich wie der Speicher in Java verwaltet wird, muss ein Techniker in kritischen Anwendungen möglicherweise so viele Optimierungen wie möglich durchführen, um die Latenz zu minimieren und den Durchsatz zu maximieren. Obwohl es unmöglich ist, explizit zu steuern, wann die Speicherbereinigung in der JVM ausgelöst wird , ist es möglich zu beeinflussen, wie sie in Bezug auf die von uns erstellten Objekte auftritt.

Java stellt uns Referenzobjekte zur Verfügung, um die Beziehung zwischen den von uns erstellten Objekten und dem Garbage Collector zu steuern.

Standardmäßig wird jedes Objekt, das wir in einem Java-Programm erstellen, stark von einer Variablen referenziert:

StringBuilder sb = new StringBuilder();

Im obigen Snippet erstellt das neue Schlüsselwort ein neues StringBuilder- Objekt und speichert es auf dem Heap. Die Variable sb speichert dann einen starken Verweis auf dieses Objekt. Für den Garbage Collector bedeutet dies, dass das jeweilige StringBuilder- Objekt aufgrund eines starken Verweises von sb überhaupt nicht zur Erfassung berechtigt ist . Die Geschichte ändert sich nur, wenn wir jdn wie folgt aufheben :

sb = null;

Nach dem Aufrufen der obigen Zeile kann das Objekt erfasst werden.

Wir können diese Beziehung zwischen dem Objekt und dem Garbage Collector ändern, indem wir es explizit in ein anderes Referenzobjekt einschließen, das sich im Paket java.lang.ref befindet .

Ein weicher Verweis auf das obige Objekt kann wie folgt erstellt werden:

StringBuilder sb = new StringBuilder(); SoftReference sbRef = new SoftReference(sb); sb = null;

Im obigen Snippet haben wir zwei Verweise auf das StringBuilder- Objekt erstellt. Die erste Zeile erstellt eine starke Referenz sb und die zweite Zeile erstellt eine weiche Referenz sbRef . Die dritte Zeile sollte das Objekt für die Sammlung geeignet machen, aber der Garbage Collector wird die Sammlung aufgrund von sbRef verschieben .

Die Geschichte wird sich nur ändern, wenn der Speicher knapp wird und die JVM kurz davor steht , einen OutOfMemory- Fehler auszulösen . Mit anderen Worten, Objekte mit nur weichen Referenzen werden als letzter Ausweg zur Wiederherstellung des Speichers gesammelt.

Eine schwache Referenz kann auf ähnliche Weise mit der WeakReference- Klasse erstellt werden. Wenn sb auf null gesetzt ist und das StringBuilder- Objekt nur eine schwache Referenz hat, hat der Garbage Collector der JVM absolut keine Kompromisse und sammelt das Objekt sofort im nächsten Zyklus.

Eine Phantomreferenz ähnelt einer schwachen Referenz, und ein Objekt mit nur Phantomreferenzen wird ohne Wartezeit erfasst. Phantomreferenzen werden jedoch in die Warteschlange gestellt, sobald ihre Objekte gesammelt wurden. Wir können die Referenzwarteschlange abfragen, um genau zu wissen, wann das Objekt gesammelt wurde.

Q13. Angenommen, wir haben eine Rundreferenz (zwei Objekte, die sich gegenseitig referenzieren). Könnte ein solches Objektpaar für die Müllabfuhr in Frage kommen und warum?

Ja, ein Objektpaar mit einem Zirkelverweis kann für die Speicherbereinigung in Frage kommen. Dies liegt daran, wie der Garbage Collector von Java Zirkelverweise verarbeitet. Objekte werden nicht als aktiv betrachtet, wenn sie einen Verweis auf sie haben, sondern wenn sie durch Navigieren im Objektdiagramm ausgehend von einem Garbage Collection-Stamm (einer lokalen Variablen eines Live-Threads oder eines statischen Felds) erreichbar sind. Wenn ein Objektpaar mit einem Zirkelverweis von keiner Wurzel aus erreichbar ist, wird es als für die Speicherbereinigung geeignet angesehen.

Q14. Wie werden Strings im Speicher dargestellt?

Eine String- Instanz in Java ist ein Objekt mit zwei Feldern: einem char [] -Wertfeld und einem int-Hash- Feld. Das Wertefeld ist ein Array von Zeichen, die die Zeichenfolge selbst darstellen, und das Hash- Feld enthält den HashCode einer Zeichenfolge, die mit Null initialisiert, während des ersten Aufrufs von hashCode () berechnet und seitdem zwischengespeichert wird. Als merkwürdiger Randfall muss ein HashCode eines Strings bei jedem Aufruf von hashCode () neu berechnet werden, wenn er einen Wert von Null hat .

Wichtig ist, dass eine String- Instanz unveränderlich ist: Sie können das zugrunde liegende char [] -Array nicht abrufen oder ändern . Ein weiteres Merkmal von Strings ist, dass die statischen konstanten Strings in einen String-Pool geladen und zwischengespeichert werden. Wenn Ihr Quellcode mehrere identische String- Objekte enthält, werden diese zur Laufzeit alle durch eine einzelne Instanz dargestellt.

Q15. Was ist ein Stringbuilder und was sind seine Anwendungsfälle? Was ist der Unterschied zwischen dem Anhängen eines Strings an einen Stringbuilder und dem Verketten von zwei Strings mit einem + -Operator? Wie unterscheidet sich Stringbuilder von Stringbuffer?

Mit StringBuilder können Sie Zeichenfolgen bearbeiten, indem Sie Zeichen und Zeichenfolgen anhängen, löschen und einfügen. Dies ist eine veränderbare Datenstruktur im Gegensatz zur unveränderlichen String- Klasse.

Beim Verketten von zwei String- Instanzen wird ein neues Objekt erstellt und Strings kopiert. Dies könnte einen riesigen Garbage Collector-Overhead verursachen, wenn wir eine Zeichenfolge in einer Schleife erstellen oder ändern müssen. Mit StringBuilder können String-Manipulationen wesentlich effizienter gehandhabt werden.

StringBuffer unterscheidet sich von StringBuilder darin, dass es threadsicher ist. Wenn Sie eine Zeichenfolge in einem einzelnen Thread bearbeiten müssen , verwenden Sie stattdessen StringBuilder .

3. Fazit

In diesem Artikel haben wir einige der häufigsten Fragen behandelt, die häufig in Java-Ingenieurinterviews auftreten. Fragen zur Speicherverwaltung werden hauptsächlich für Senior Java Developer-Kandidaten gestellt, da der Interviewer erwartet, dass Sie nicht triviale Anwendungen erstellt haben, die häufig von Speicherproblemen geplagt werden.

Dies sollte nicht als vollständige Liste von Fragen betrachtet werden, sondern als Startrampe für weitere Forschung. Wir von Baeldung wünschen Ihnen viel Erfolg bei den kommenden Interviews.

Weiter » Fragen zum Java Generics-Interview (+ Antworten) « Vorherige Fragen zum Java 8-Interview (+ Antworten)