Heim  >  Artikel  >  Java  >  Verstehen Sie den Java-Garbage-Collection-Mechanismus

Verstehen Sie den Java-Garbage-Collection-Mechanismus

WBOY
WBOYnach vorne
2023-04-24 14:10:07956Durchsuche

Bevor ich über Speichersätze und Kartenlisten spreche, möchte ich Ihnen zunächst das Thema der generationsübergreifenden Referenz vorstellen.

Verstehen Sie den Java-Garbage-Collection-Mechanismus

Angenommen, Sie möchten eine Sammlung (Minor GC) durchführen, die auf den Bereich der neuen Generation beschränkt ist, aber das Instanzobjekt 1 der neuen Generation wird in der alten Generation referenziert, um alle Objekte in herauszufinden Dieser Bereich (neue Generation) Überlebende Objekte müssen zusätzlich zu den festen GC-Wurzeln alle Objekte der gesamten alten Generation durchlaufen, um die Richtigkeit der Ergebnisse der Erreichbarkeitsanalyse sicherzustellen, und umgekehrt. Obwohl die Lösung, alle Objekte der gesamten alten Generation zu durchlaufen, theoretisch machbar ist, wird sie zweifellos eine große Leistungsbelastung für das Speicherrecycling mit sich bringen.

Tatsächlich handelt es sich nicht nur um das Problem der generationsübergreifenden Referenzen zwischen der neuen Generation und der alten Generation. Alle Garbage Collectors, die am Verhalten der partiellen Bereichssammlung (Partial GC) beteiligt sind, wie z. B. G1-, ZGC- und Shenandoah-Collectors, werden dies tun Seien Sie ehrlich. Das gleiche Problem.

Wie können wir also generationsübergreifende Bezüge lösen?

Erstens machen generationsübergreifende Zitate im Vergleich zu Zitaten derselben Generation nur einen sehr geringen Anteil aus. Der Grund dafür ist, dass Objekte, auf die über Generationen hinweg verwiesen wird, tendenziell gleichzeitig überleben oder sterben sollten (z. B. wenn ein Objekt der neuen Generation über eine generationsübergreifende Referenz verfügt, weil das Objekt der alten Generation schwer zu sterben ist, ermöglicht diese Referenz das neue Das zu sammelnde Generationsobjekt bleibt erhalten, wenn es gesammelt wird, und wird dann mit zunehmendem Alter zur alten Generation befördert, wobei auch generationsübergreifende Verweise eliminiert werden.

Dem oben Gesagten zufolge besteht keine Notwendigkeit, die gesamte alte Generation nach einer kleinen Anzahl generationsübergreifender Referenzen zu durchsuchen. Es besteht keine Notwendigkeit, Platz zu verschwenden, um aufzuzeichnen, ob jedes Objekt vorhanden ist und welche generationsübergreifenden Referenzen vorhanden sind. Sie müssen lediglich eine globale Datenstruktur für die neue Generation erstellen (diese Struktur wird als „Remembered Set“ bezeichnet). Diese Struktur unterteilt die alte Generation in mehrere kleine Blöcke und identifiziert, welchen Teil des Speichers die alte Generation haben wird Generationenübergreifende Referenzen. Wenn anschließend Minor GC auftritt, werden nur Objekte in kleinen Speicherblöcken, die generationsübergreifende Referenzen enthalten, zum Scannen zu GCRoots hinzugefügt. Obwohl diese Methode die Richtigkeit der aufgezeichneten Daten aufrechterhalten muss, wenn das Objekt seine Referenzbeziehung ändert (z. B. sich selbst oder ein bestimmtes Attribut zuweist), was einen gewissen Laufzeitaufwand erhöht, ist es dennoch kostengünstiger als das Scannen der gesamten alten Generation während der Sammlung.

Lassen Sie uns diesen globalen Datenstrukturspeichersatz vorstellen.

Speichersatz

Speichersatz ist eine abstrakte Datenstruktur, die zum Aufzeichnen einer Sammlung von Zeigern von Nicht-Sammelbereichen auf Sammelbereiche verwendet wird. Wenn wir Effizienz und Kosten nicht berücksichtigen, kann die einfachste Implementierung alle Objektarrays verwenden, die generationsübergreifende Referenzen im Nicht-Sammlungsbereich enthalten, um diese Datenstruktur zu implementieren, wie im folgenden Code gezeigt:

//以对象指针来实现记忆集的伪代码
Class RememberedSet {
	Object[] set[OBJECT_INTERGENERATIONAL_REFERENCE_SIZE]; 
}

Alle solchen Datensätze enthalten Cross- Generierungsreferenzen Die Objektimplementierungslösung ist im Hinblick auf den Platzbedarf und die Wartungskosten recht teuer. In einem Garbage-Collection-Szenario muss der Collector nur den Speichersatz verwenden, um zu bestimmen, ob ein bestimmter Nicht-Sammelbereich einen Zeiger hat, der auf den Sammelbereich zeigt. Er muss nicht alle Details dieser generationsübergreifenden Zeiger kennen. Wenn Designer den Speichersatz implementieren, können sie eine gröbere Datensatzgranularität wählen, um Speicher- und Wartungskosten des Speichersatzes zu sparen. Nachfolgend sind einige Datensatzgenauigkeiten aufgeführt, aus denen Sie wählen können (natürlich können Sie auch außerhalb dieses Bereichs wählen):

  • Wortlängengenauigkeit: Jeder Datensatz ist auf die Länge eines Maschinenworts genau (d. h. die Anzahl der Adressierungsbits des Prozessors). , wie bei 32 oder 64 Bit üblich, diese Genauigkeit bestimmt die Länge der Zeiger, die von der Maschine verwendet werden, um auf physische Speicheradressen zuzugreifen), dieses Wort enthält generationsübergreifende Zeiger.

  • Objektgenauigkeit: Jeder Datensatz ist auf ein Objekt genau, und es gibt Felder im Objekt, die generationsübergreifende Zeiger enthalten.

  • Kartengenauigkeit: Jeder Datensatz ist auf einen Speicherbereich genau, und in diesem Bereich gibt es Objekte, die generationsübergreifende Zeiger enthalten.

Die oben genannte dritte Art der „Kartenpräzision“ bezieht sich auf die Verwendung einer Methode namens „Kartentabelle“ zur Implementierung von Speichersätzen, die derzeit auch die am häufigsten verwendete Implementierungsform von Speichersätzen ist.

Welche Beziehung besteht zwischen Kartenliste und Speichersatz?

Als ich den Speichersatz zuvor vorgestellt habe, habe ich erwähnt, dass der Speichersatz tatsächlich eine „abstrakte“ Datenstruktur ist. Abstraktion bedeutet, dass sie nur die Verhaltensabsicht des Speichersatzes definiert und nicht die spezifische Implementierung seines Verhaltens. Die Kartentabelle ist eine spezifische Implementierung des Speichersatzes, die die Aufzeichnungsgenauigkeit des Speichersatzes, die Zuordnungsbeziehung zum Heapspeicher usw. definiert. Die Beziehung zwischen dem Speichersatz und der Kartentabelle kann in Analogie zur Beziehung zwischen Map und HashMap in Java (dh der Beziehung zwischen Schnittstellen und Implementierungsklassen) verstanden werden.

Lassen Sie uns im Detail über die spezifische Implementierung der Speichersatz-Kartentabelle sprechen

Kartentabelle

Die Kartentabelle wird mithilfe eines Byte-Arrays CARD_TABLE[] implementiert. Jedes Element entspricht einem Speicherblock einer bestimmten Größe im Speicherbereich Es identifiziert jedes Element. Ein Speicherblock wird als Kartenseite bezeichnet. Die von Hotspot verwendete Kartenseite ist 2^9 groß, was 512 Bytes entspricht. Wie in der Abbildung unten gezeigt

Verstehen Sie den Java-Garbage-Collection-Mechanismus

Auf diese Weise können wir einen bestimmten Bereich nach Kartenseiten unterteilen. Wenn wir nun eine Speicherbereinigung für den Bereich der neuen Generation durchführen möchten, können wir den Bereich der alten Generation als einen betrachten Kartenseite und eine Karte. Die Seiten sind wie in der Abbildung unten dargestellt aufgeteilt.

Verstehen Sie den Java-Garbage-Collection-Mechanismus

Wie in der Abbildung gezeigt, ist die erste Position der entsprechenden Kartentabelle 1, da in Kartenseite1 eine generationsübergreifende Referenz vorhanden ist, die auf die neue Generation verweist, was darauf hinweist, dass sich im Seitenbereich generationsübergreifende Anwendungsobjekte befinden .

  • Kartentischwinkel: Da es auf Seite 1 generationsübergreifende Trinkobjekte gibt, wird die erste Position, die dem Kartentisch entspricht, als 1 aufgezeichnet, was darauf hinweist, dass das Element von Seite 1 verschmutzt ist.

  • Speicherrecyclingwinkel: Da die erste Position der Kartentabelle 1 ist, bedeutet dies, dass sich im Seitenbereich generationsübergreifende Anwendungsobjekte befinden und dieser Bereich während der Speicherbereinigung gescannt werden muss.

Der Speicher einer Kartenseite enthält normalerweise mehr als ein Objekt. Solange sich im Feld eines (oder mehrerer) Objekte auf der Kartenseite ein generationsübergreifender Zeiger befindet, ist der Wert des Array-Elements der Die entsprechende Kartentabelle wird als 1 markiert. Dieses Element wird als schmutzig (Dirty) bezeichnet. Wenn nicht, wird es als 0 markiert. Wenn eine Speicherbereinigung auftritt, können Sie, solange die schmutzigen Elemente in der Kartentabelle herausgefiltert werden, leicht herausfinden, welche Kartenseitenspeicherblöcke generationsübergreifende Zeiger enthalten, diese zu GC Roots hinzufügen und gemeinsam scannen. Dadurch entfällt die Notwendigkeit, die gesamte alte Generation zu scannen, und der Scanbereich von GC Roots wird erheblich reduziert.

Das obige ist der detaillierte Inhalt vonVerstehen Sie den Java-Garbage-Collection-Mechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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