Nach meinem Verständnis bereinigt die Garbage Collection in Java einige Objekte, wenn nichts anderes auf dieses Objekt zeigt.
Meine Frage ist, was passiert, wenn wir so etwas haben:
class Node {
public object value;
public Node next;
public Node(object o, Node n) { value = 0; next = n;}
}
//...some code
{
Node a = new Node("a", null),
b = new Node("b", a),
c = new Node("c", b);
a.next = c;
} //end of scope
//...other code
a
, b
Und c
soll Müll gesammelt werden, aber sie sind alle von anderen Objekten verwiesen werden.
Wie geht die Java-Garbage Collection damit um? (oder ist es einfach ein Speicherverlust?)
java
garbage-collection
AlexeyMK
quelle
quelle
Antworten:
Javas GC betrachtet Objekte als "Müll", wenn sie nicht über eine Kette erreichbar sind, die an einem Garbage Collection-Stamm beginnt, sodass diese Objekte gesammelt werden. Auch wenn Objekte zueinander zeigen, um einen Zyklus zu bilden, sind sie immer noch Müll, wenn sie von der Wurzel abgeschnitten sind.
Weitere Informationen finden Sie im Abschnitt zu nicht erreichbaren Objekten in Anhang A: Die Wahrheit über die Garbage Collection in Java Platform Performance: Strategien und Taktiken .
quelle
ja Java Garbage Collector verarbeitet Zirkelverweise!
Es gibt spezielle Objekte, die als Garbage-Collection-Roots (GC-Roots) bezeichnet werden. Diese sind immer erreichbar, ebenso wie jedes Objekt, das sie an seiner eigenen Wurzel hat.
Eine einfache Java-Anwendung hat die folgenden GC-Wurzeln:
Um festzustellen, welche Objekte nicht mehr verwendet werden, führt die JVM zeitweise einen sehr treffend als Mark-and-Sweep-Algorithmus bezeichneten Algorithmus aus . Es funktioniert wie folgt
Wenn also ein Objekt von den GC-Wurzeln aus nicht erreichbar ist (selbst wenn es selbstreferenziert oder zyklisch referenziert ist), wird es einer Speicherbereinigung unterzogen.
Natürlich kann dies manchmal zu einem Speicherverlust führen, wenn der Programmierer vergisst, ein Objekt zu dereferenzieren.
Quelle: Java Memory Management
quelle
Ein Garbage Collector startet an einigen "Root" -Sätzen, die immer als "erreichbar" gelten, z. B. den CPU-Registern, dem Stapel und den globalen Variablen. Es funktioniert, indem Zeiger in diesen Bereichen gefunden werden und rekursiv alles gefunden wird, auf das sie zeigen. Sobald alles gefunden ist, ist alles andere Müll.
Natürlich gibt es einige Variationen, hauptsächlich aus Gründen der Geschwindigkeit. Beispielsweise sind die meisten modernen Garbage Collectors "Generationen", was bedeutet, dass sie Objekte in Generationen unterteilen. Wenn ein Objekt älter wird, dauert der Garbage Collector zwischen den Versuchen, herauszufinden, ob dieses Objekt noch gültig ist oder nicht, immer länger - Es beginnt nur anzunehmen, dass die Chancen, wenn es lange gelebt hat, ziemlich gut sind, dass es noch länger leben wird.
Trotzdem bleibt die Grundidee dieselbe: Alles basiert darauf, von einigen Grundsätzen auszugehen, die für selbstverständlich gehalten werden, und dann alle Zeiger zu verfolgen, um herauszufinden, was sonst noch verwendet werden könnte.
Interessant beiseite: Mögen Menschen oft überrascht sein, wie ähnlich dieser Teil eines Garbage Collectors und der Code für das Marshalling von Objekten für Dinge wie Remoteprozeduraufrufe sind. In jedem Fall gehen Sie von einem Stammsatz von Objekten aus und suchen nach Zeigern, um alle anderen Objekte zu finden, auf die ...
quelle
Du hast Recht. Die von Ihnen beschriebene spezifische Form der Speicherbereinigung wird als " Referenzzählung " bezeichnet. Die Funktionsweise (zumindest konzeptionell werden die meisten modernen Implementierungen der Referenzzählung tatsächlich ganz anders implementiert) sieht im einfachsten Fall folgendermaßen aus:
Und diese einfache Strategie hat genau das Problem, das Sie beschreiben: Wenn A auf B verweist und B auf A verweist, können beide Referenzzählungen niemals kleiner als 1 sein, was bedeutet, dass sie niemals gesammelt werden.
Es gibt vier Möglichkeiten, um mit diesem Problem umzugehen:
Übrigens ist die andere wichtige Möglichkeit, einen Garbage Collector zu implementieren (und darauf habe ich bereits einige Male oben hingewiesen), die Ablaufverfolgung . Ein Tracing Collector basiert auf dem Konzept der Erreichbarkeit . Sie beginnen mit einem Stammsatz, von dem Sie wissen , dass er immer erreichbar ist (z. B. globale Konstanten oder die
Object
Klasse, der aktuelle lexikalische Bereich, der aktuelle Stapelrahmen), und verfolgen von dort aus alle Objekte, die vom Stammsatz aus erreichbar sind Alle Objekte, die von den Objekten aus erreichbar sind, die vom Stammsatz aus erreichbar sind, usw., bis Sie den transitiven Abschluss haben. Alles, was nicht in dieser Schließung ist, ist Müll.Da ein Zyklus nur in sich selbst erreichbar ist, aber nicht über den Stammsatz erreichbar ist, wird er gesammelt.
quelle
Die Java-GCs verhalten sich nicht so, wie Sie es beschreiben. Es ist genauer zu sagen, dass sie von einer Basismenge von Objekten ausgehen, die häufig als "GC-Wurzeln" bezeichnet werden, und alle Objekte sammeln, die von einer Wurzel aus nicht erreichbar sind.
GC-Wurzeln umfassen Dinge wie:
In Ihrem Fall gibt es also keine GC-Wurzeln mehr, die direkt oder indirekt einen Verweis auf einen Ihrer drei Knoten enthalten, sobald die lokalen Variablen a, b und c am Ende Ihrer Methode den Gültigkeitsbereich verlassen haben Sie sind für die Müllabfuhr berechtigt.
Der Link von TofuBeer enthält weitere Details, wenn Sie dies wünschen.
quelle
Dieser Artikel (nicht mehr verfügbar) befasst sich ausführlich mit dem Garbage Collector (konzeptionell ... gibt es mehrere Implementierungen). Der relevante Teil Ihres Beitrags ist "A.3.4 Nicht erreichbar":
quelle
Garbage Collection bedeutet normalerweise nicht "ein Objekt bereinigen, wenn nichts anderes auf dieses Objekt" zeigt "(das ist Referenzzählung). Garbage Collection bedeutet ungefähr, Objekte zu finden, die vom Programm aus nicht erreichbar sind.
In Ihrem Beispiel können sie nach dem Verlassen des Gültigkeitsbereichs von a, b und c vom GC erfasst werden, da Sie nicht mehr auf diese Objekte zugreifen können.
quelle
Bill hat Ihre Frage direkt beantwortet. Wie Amnon sagte, ist Ihre Definition der Speicherbereinigung nur eine Referenzzählung. Ich wollte nur hinzufügen, dass selbst sehr einfache Algorithmen wie Markieren, Sweepen und Kopieren leicht zirkuläre Referenzen verarbeiten können. Also nichts Magisches daran!
quelle