-
Hintergrund
der Erfindung
-
1. Gebiet
der Erfindung
-
Die
vorliegende Erfindung betrifft im Allgemeinen die Verwaltung eines
dynamisch zugeteilten Speichers in einem Computersystem. Im Besonderen
betrifft die Erfindung ein Nachverfolgen von Referenzen zwischen
separaten Abschnitten eines mit einem Computersystem verknüpften Speichers,
so dass eine automatische Speicherungsrückforderung lokal durchgeführt werden
kann.
-
2. Beschreibung des Standes
der Technik
-
Die
Menge eines mit einem Computersystem verknüpften Speichers ist typischerweise
begrenzt. Als solcher muss Speicher im Allgemeinen erhalten und
wiederverwertet werden. Viele Computerprogrammiersprachen ermöglichen
es Softwareentwicklern, Speicher dynamisch innerhalb eines Computersystems
zuzuteilen. Manche Programmiersprachen erfordern eine explizite
manuelle Freigabe bzw. Deallokation zuvor zugeteilten Speichers,
was kompliziert und fehleranfällig
sein kann. Sprachen, die eine explizite manuelle Speicherverwaltung
erfordern, umfassen die C- und C++-Programmiersprachen. Andere Programmiersprachen
nutzen eine automatische Speicherungsrückforderung zum Rückfordern
von Speicher, der nicht länger
erforderlich ist zum Sicherstellen des ordnungsgemäßen Betriebs
von Computerprogrammen, die Speicher von dem Rückforderungssystem zuteilen.
Solche automatischen Speicherrückforderungssysteme
fordern Speicher ohne explizite Anweisungen oder Aufrufe von Computerprogrammen
zurück,
die zuvor den Speicher nutzten.
-
In
objektorientierten oder objektbasierten Systemen wird auf die typische
Einheit einer Speicherzuteilung üblicherweise
als ein Objekt oder ein Speicherobjekt verwiesen, wie es der Fachmann richtig
einschätzen
wird. Auf Objekte, die in Verwendung sind, wird im Allgemeinen als „lebende" Objekte verwiesen,
wohingegen auf Objekte, die nicht länger benötigt werden zum korrekten Ausführen von
Computerprogrammen typischerweise als „Abfall"-Objekte
bzw. „wertlose" Datenobjekte verwiesen
wird. Die Handlung eines Rückforderns
von Abfallobjekten wird im Allgemeinen als eine Speicherbereinigung bezeichnet,
während
ein automatisches Speicherungsrückforderungssystem
häufig
als ein Speicherbereiniger bezeichnet wird. Computerprogramme, die
automatische Speicherrückforderungssysteme verwenden,
sind aufgrund der Tatsache, dass solche Computerprogramme lebende
Speicherobjekte während
einer Ausführung ändern können, als
Mutatoren bekannt. Computerprogramme, die in Sprachen wie beispielsweise
der JavaTM-Programmiersprache (entwickelt
von Sun Microsystems, Inc. von Palo Alto, Kalifornien) und der Smalltalk-Programmiersprache
geschrieben sind, verwenden eine Speicherbereinigung zum automatischen
Speicherverwalten.
-
Objekte
enthalten typischerweise Referenzen auf andere Objekte. Als solches
wird ein Bereich eines Computerspeichers, der durch einen Speicherbereiniger
verwaltet wird, im Allgemeinen eine Menge von Objekten enthalten,
die einander referenzieren. 1 ist eine
diagrammartige Darstellung eines Bereiches eines Computerspeichers,
der Objekte enthält.
Ein verwalteter Bereich eines Speichers 10, der typischerweise
ein mit einem Computersystem verknüpfter Heap bzw. Stapel ist, enthält Objekte 20. Im
Allgemeinen kann ein Objekt 20 durch andere Objekte 20 referenziert
werden. Beispielsweise hat Objekt 20a einen Zeiger 24a auf
Objekt 20b. Objekt 20c hat auch einen Zeiger 24b auf
Objekt 20b. Als solches wird Objekt 20b durch
beide Objekte 20a und 20c referenziert.
-
Eine
Zahl von außen
verweist bzw. referenziert in den Speicher 10. Wie gezeigt,
enthält
eine feste Wurzel 30, die extern zum Speicher 10 ist,
Zeiger 34 auf Objekte, zum Beispiel Objekte 20a und 20c,
die sich in Speicher 10 befinden. Alle Objekte, wie zum
Beispiel Objekte 20a–d,
die durch Folgen der Referenzen von der festen Wurzel 30 erreichbar sein
können,
werden als lebende Objekte betrachtet. Alternativ wird Objekt 20e,
welches nicht durch Folgen der Referenzen von der festen Wurzel 30 erreichbar
ist, als ein Abfallobjekt charakterisiert.
-
Speicherbereiniger
werden typischerweise implementiert zum Identifizieren von Abfallobjekten, so
wie Objekt 20e. Im Allgemeinen können Speicherbereiniger mit
Verwenden einer Anzahl unterschiedlicher Algorithmen arbeiten. Konventionelle
Speicherbereinigungsalgorithmen enthalten Referenzzählsammler,
Markierungssäuberungssammler
und Kopiersammler. Wie der Fachmann richtig erkennen wird, müssen während einer
Speicherbereinigung, wenn Objekte 20 bewegt werden, Referenzen
auf Objekte 20 demgemäß eingestellt
werden.
-
Es
ist oft günstig,
einen verwalteten Speicherbereich in kleinere Abschnitte aufzuteilen, um
zu ermöglichen,
dass eine Speicherbereinigung lokal in einem Bereich zu einer Zeit
durchgeführt wird.
Ein Speicherpartitionierungsschema ist die Generationsspeicherbereinigung,
in welcher Objekte basierend auf ihren Lebenszeiten, wie von der
Zeit gemessen, als die Objekte erschaffen wurden, getrennt werden.
Es ist beobachtet worden, dass „jüngere" Objekte wahrscheinlicher Abfall werden
als „ältere" Objekte. Als solches
kann eine Generationsspeicherbereinigung verwendet werden zum Erhöhen der
Gesamteffizienz einer Speicherrückforderung.
-
2 ist
eine diagrammartige Darstellung einer Schnittstelle zwischen einer
Wurzel und einem Speicher, der in eine neue Generation und eine
alte Generation partitioniert ist. Ein Speicher 110, der
typischerweise ein mit einem Computersystem verknüpfter Heap
bzw. Stapel ist, enthält
eine neue Generation 110a und eine alte Generation 110b.
Eine feste Wurzel 114 oder ein globales Objekt, das Objekte
in einer oder beiden der neuen Generation 110a und der
alten Generation 110b referenziert, enthält Zeiger 116 auf
Objekte 120 in der neuen Generation 110a, wie
gezeigt. Wurzel 114 kann sich auf einem Stack bzw. Stapelspeicher
befinden, wie es vom Fachmann richtig eingeschätzt werden wird.
-
Manche
Objekte 120 innerhalb der neuen Generation 110a,
wie zum Beispiel Objekt 120a, können als Wurzeln betrachtet
werden, da Objekt 120 als lebend angenommen wird und einen
Zeiger 122 auf ein anderes Objekt 120d enthält. Wenn
ein Objekt für eine
neue Generation 126 lebendig ist und auf ein Objekt für eine alte
Generation 128 zeigt, „sammelt" eine in der alten Generation 110b durchgeführte Speicherbereinigung
im Allgemeinen nicht Objekt 128. Wenn jedoch das Objekt
für eine
neue Generation 126 tot ist, wird eine in der neuen Generation 110a durchgeführte Speicherbereinigung
darin resultieren, dass das Objekt für eine alte Generation 128 unerreichbar
wird, da auf das Objekt für
eine alte Generation 128 nicht durch irgendein anders Objekt
gezeigt werden wird. Wenn das Objekt für eine alte Generation 128 unerreichbar
ist, dann wird die in der alten Generation 110b durchgeführte Speicherbereinigung in
der Sammlung des Objekts für
eine alte Generation 128 resultieren. Es sollte erkannt
werden, dass Zeiger 130, der zwischen Objekt für eine neue Generation 126 und
Objekt für
eine alte Generation 128 zeigt, als ein Zwischengenerationszeiger
betrachtet wird, da Zeiger 130 sowohl die neue Generation 110a als
auch die alte Generation 110b überspannt. Wenn Zeiger 130 vom
Objekt für
eine neue Generation 126 auf Objekt für eine alte Generation 128 zeigt,
wird Objekt für
eine alte Generation 128 als Dauerabfall betrachtet, da
Objekt für
eine alte Generation 128 nicht mit Verwenden einer Speicherbereinigung
für eine
neue Generation gesammelt werden kann.
-
Ein
anders Speicherpartitionierungsschema schließt ein Trennen des Speichers
in kleinere Bereiche ein, um die Zeitmenge zu reduzieren, die für eine durchzuführende einzelne
Speicherbereinigung erforderlich ist. Durch eine Speicherbereinigung
verursachte Pausen führen
häufig
zu einem Stören
eines verknüpften
Mutators und sind deshalb unerwünscht. In
manchen Systemen kann der Speicherbereiniger fähig sein zum Bereitstellen
einer garantierten kleinen maximalen Pausendauer. Solche verknüpften Speicherbereiniger
sind als Echtzeit-Speicherbereiniger
bekannt. In anderen Systemen kann der Speicherbereiniger versuchen,
Pausenzeiten klein zu halten, aber er kann in manchen Situationen
versagen, dieses zu tun. Speicherbereiniger, die versuchen Pausenzeiten
klein zu halten, sind als nicht-störende oder inkrementelle Speicherbereiniger
bekannt.
-
Um
auf einem individuellen Speichergebiet zu arbeiten, muss ein Speicherbereiniger
Kenntnis über
sämtliche
Referenzen in diesem Bereich haben. Referenzen in einen Bereich
werden als Wurzeln für diesen
Bereich bezeichnet. Es sollte erkannt werden, dass Wurzeln sowohl
externe Referenzen, zum Beispiel feste Wurzeln, als auch Referenzen
von anderen Bereichen eines Computerspeichers enthalten können. Demgemäß stellen
Speicherbereiniger im Allgemeinen Mechanismen zum Finden und Nachverfolgen
von Wurzeln oder Referenzen bereit.
-
Ein
Verfahren zum Lokalisieren von Referenzen in einem Speicherbereich
schließt
ein Absuchen durch alle Objekte im Speicher ein. Für die meisten Systeme
ist ein Absuchen durch alle Objekte im Speicher unerschwinglich
zeitverbrauchend. Als solches ist häufig ein kunstreicheres Nachverfolgungsschema
erforderlich.
-
Immer
wenn ein Mutator eine Referenz in ein Objekt speichert, kann ein
zusätzliches
Verarbeiten implementiert sein, um Referenzen für Speicherbereinigungszwecke
nachzuverfolgen. Dieses zusätzliche
Verarbeiten ist als eine Schreibschranke oder eine Speicherungsprüfung bekannt.
Damit die Effizienz des Mutators auf einer annehmbaren Ebene gehalten
werden kann, müssen
die mit der Schreibschranke verknüpften Kosten so gering wie
möglich gehalten
werden.
-
Ein
Weg zum Nachverfolgen von Referenzen in einen Speicherbereich schließt ein Aufrechterhalten
einer Menge ein, welche sämtliche
Wurzeln für diesen
Bereich hält.
Solch eine Menge ist im Allgemeinen bekannt als eine Gedächtnismenge
für den Bereich.
Die verknüpfte
Schreibschranke wird erfassen, wenn eine Referenz in einen Bereich
gespeichert wird. Demgemäß wird die
Schreibschranke den Ort der Referenz in die mit dem Bereich verknüpfte Gedächtnismenge
einfügen.
-
3 ist
eine diagrammartige Darstellung von Zeigern zwischen Objekten in
einer neuen Generation und Objekten in einer alten Generation, die
mit Verwenden einer Gedächtnismenge
nachverfolgt werden. Ein Speicher 302 ist in eine neue
Generation 302a und eine alte Generation 302b aufgeteilt.
Eine Gedächtnismenge 304 wird
verwendet zum Nachverfolgen von Zeigern 314, die von Objekten
für eine
alte Generation 310 auf Objekte eine neue Generation 312 zeigen.
Die Adresse vom Objekt für
eine alte Generation 310a wird in Gedächtnismenge 304 gespeichert,
da Objekt für
eine alte Generation 310a Zeiger 314a auf Objekt
für eine
neue Generation 312b enthält. Ähnlich wird die Adresse vom
Objekt für
eine alte Generation 310b, das Zeiger 314b und 314c auf Objekte
für eine
neue Generation 312b bzw. 312a enthält, ebenso
in Gedächtnismenge 304 gespeichert.
-
Eine
Lokalisierung von Wurzeln zur Speicherbereinigung ist unkompliziert,
wenn eine Gedächtnismenge
verwendet wird, da die Gedächtnismenge
sämtliche
Wurzeln enthalten wird. Jedoch ist die Verwendung einer Schreibschranke
oft teuer, da Zusatzspeicher erforderlich sein kann. Wenn ein neuer
Ort in eine Gedächtnismenge
eingefügt
werden soll, ist es auch möglich,
dass der bestimmte Ort bereits in der Gedächtnismenge vorliegen kann.
Ein Überprüfen der
Gedächtnismenge
auf Duplikatorte vor einem Einfügen
eines Ortes ist teuer. Andererseits kann ein Eliminieren einer Prüfung auf
Duplikatorte bewirken, dass die Gedächtnismenge unnötig groß anwächst. Der
Fachmann wird richtig einschätzen,
dass es im Allgemeinen keine obere Grenze für die Anzahl von Duplikaten
gibt, die eine Gedächtnismenge
halten kann. Wenn eine Referenz auf einen Ort gespeichert werden
soll, enthält
der Ort oft bereits eine vorherige Referenz. Als ein Ergebnis wird
Gedächtnismengeneintrag,
der mit dem Ort vor einer Speicheroperation verknüpft ist,
im Allgemeinen, der Speicheroperation folgend, ungültig werden.
Ein Entfernen eines alten Eintrags kann sich als eine kostenträchtige Operation
in manchen Situationen erweisen, wohingegen es in der Belegung von Überschussspeicher
durch eine Gedächtnismenge
resultieren kann, den alten Eintrag am Platz zu lassen.
-
Ein
anderes Schema, das häufig
zum Nachverfolgen von Referenzen in einen Speicherbereich verwendet
wird, ist als Kartenmarkieren bekannt. Im Allgemeinen schließt Kartenmarkieren
konzeptuell ein Aufteilen eines Speichers in relativ kleine Teile ein,
die Karten genannt werden. Ein Speicherbereiniger wird dann einen
Array von Bits mit einem Eintrag pro Karte zuteilen. Wenn eine Referenz
gespeichert wird, wird die Schreibschranke den entsprechenden Karten-Array-Eintrag
berechnen und das verknüpfte Bit
setzen. Dieser Prozess ist als ein „Beschmutzen" (Dirtying) der Karte
bekannt. Es sollte erkannt werden, dass aus Effizienzgründen häufig ein
Byte- oder Word-Array anstelle eines Bit-Arrays verwendet werden.
-
Ein
Vorteil des Verwendens des Kartenmarkierens ist, dass die Schreibschranke
relativ billig ist. Ein anderer Vorteil des Verwendens des Kartenmarkierens
ist, dass die für
den Karten-Array erforderliche Speichermenge unveränderlich
ist. Jedoch ist das zum Lokalisieren von Wurzeln zur Speicherbereinigungszeit
erforderliche Verarbeiten häufig
signifikant, es wird zum Beispiel mehr Verarbeitung gefordert, als
es für
ein System gefordert würde,
welches eine Gedächtnismenge
nutzt. Der Speicherbereiniger weiß nur ungefähr, wo Referenzspeicher aufgetreten
sind. Das heißt,
der Speicherbereiniger weiß nur,
wo der Kartenmarkierungs-Array
beschmutzte Einträge
hat. Sobald ein beschmutzter Eintrag identifiziert ist, müssen die
entsprechenden Karten nach Wurzeln abgesucht werden. Obwohl ein
Absuchen einer Karte nach Wurzeln im Allgemeinen viel billiger ist
als ein Absuchen eines gesamten Speichers, ist ein Absuchen der
Karte immer noch häufig
kostenträchtig.
Es wird vom Fachmann verstanden werden, dass, wenn ein Speicherbereiniger
eine Wurzel lokalisiert, der entsprechende Karten-Array-Eintrag
beschmutzt gehalten werden muss, damit die Wurzel beim nächsten Aufrufen
der Speicherbereinigung lokalisiert werden kann.
-
Ein
kombiniertes Schema kann Gedächtnismengen
und Kartenmarkieren verwenden. Eine Schreibschranke wird ein Kartenmarkieren
wie oben beschrieben durchführen.
Jedoch wird, zur Speicherbereinigungszeit, der Speicherbereiniger
Gedächtnismengen
für jede
Karte konstruieren. Wenn die Speicherbereinigung vollendet ist,
kann der Kartenmarkierungs-Array dann gelöscht bzw. gesäubert werden.
Ein Verwenden eines Schemas für
eine kombinierte Gedächtnismenge
und ein Kartenmarkieren reduziert die für nachfolgende Speicherbereinigungen
erforderliche Absuchmenge, wodurch die Gesamteffizienz von Speicherbereinigungsprozessen
erhöht
wird. Deshalb sind ein Verfahren und eine Vorrichtung zum effizienten
Implementieren eines Schemas für
eine Kombination einer Gedächtnismenge
und eines Kartenmarkierens wünschenswert.
-
In
der Veröffentlichung „Remembered
sets can also play cards" – Position
Paper for Oopsla '93 Workshop
On Memory Management and Garbage Collection (Washington, D. C.,
Oktober 1992) XP002105705, wird ein System für eine Kombination einer Gedächtnismenge
und eines Kartenmarkierens zur Speicherbereinigung beschrieben,
das dem oben erwähnten ähnlich ist.
In diesem Hybridsystem wird Kartenmarkieren verwendet zum Nachverfolgen
von Zeigerbeständen,
und Gedächtnismengen
werden verwendet zum Zusammenfassen der interessanten Zeiger zur
Speicherbereinigung.
-
In
der Veröffentlichung „Incremental
Mature Garbage Collection Using the Train Algorithm" – Proceedings of ECOOP '95, 9th European
Conference on Object-Oriented Programming, Lecture Notes In Computer
Science, Vol. 952, Seiten 235–252,
Springer Verlag, 1995, ISBN 3-540-60160-0, 26. März 1995, XP002105706, ist eine
Implementierung des Train-Algorithmus beschrieben, einem inkrementellen
Sammlungsschema zur nicht-störenden
Rückforderung
reifen Abfalls in einem generationsbasierten Speicherverwaltungssystem.
Ein Raum für
ein reifes Objekt wird in eine Anzahl von Blöcken fester Größe aufgeteilt,
und ein Block wird bei jedem Aufruf gesammelt bzw. gereinigt. Eine
Gedächtnismengen-basierte Implementierung
wird beschrieben. Referenzen von alten auf junge Objekte werden
in einer Hash-Tabellen-basierten
Gedächtnismenge
für jedes Car
aufgezeichnet und mit Verwenden einer Schreibschranke zum Untersuchen
von Zeigerzuweisungen.
-
Gemäß einem
ersten Aspekt der Erfindung wird ein Computer-implementiertes Verfahren
zum dynamischen Verwalten eines mit einem Computersystem verknüpften Speichers
bereitgestellt, wobei der Speicher einen ersten Speicherabschnitt
und einen zweiten Speicherabschnitt enthält, der in eine Vielzahl von
Blöcken
aufgeteilt ist, wobei die Blöcke in
dem zweiten Speicherabschnitt jeweils eine verknüpfte Markierung haben, wobei
das Verfahren umfasst: Durchführen
einer ersten Speicherbereinigung in dem ersten Speicherabschnitt;
Durchführen
einer zweiten Speicherbereinigung in einem ausgewählten der
Blöcke
in dem zweiten Speicherabschnitt; Durchführen einer dritten Speicherbereinigung
in dem ausgewählten
Block in dem zweiten Speicherabschnitt, wobei die dritte Speicherbereinigung
ein Bestimmen enthält,
ob der ausgewählte
Block ein erstes Objekt enthält,
das ein zweites Objekt referenziert, das nicht in dem ausgewählten Block
enthalten ist, basierend auf wenigstens teilweise einem Status,
der durch die mit dem ausgewählten
Block verknüpfte
Markierung angegeben ist, wobei der Status eine Angabe enthält, ob die
Referenz auf das zweite Objekt gespeichert wurde, nachdem die zweite
Speicherbereinigung durchgeführt
wurde; und wenn die Markierung angibt, dass die Referenz auf das
zweite Objekt gespeichert wurde, nachdem die zweite Speicherbereinigung
durchgeführt
wurde, Zuteilen eines neuen mit der Markierung verknüpften Wurzel-Arrays,
wobei die Markierung einen Zeiger auf den neuen Wurzel-Array bereitstellt,
und wobei der neue Wurzel-Array die Adresse des Zeigers auf das
zweite Objekt hält.
-
Gemäß einem
weiteren Aspekt der Erfindung wird ein Computersystem bereitgestellt
mit: einer Datenverarbeitungsvorrichtung; und einem Speicher in
Verbindung mit der Datenverarbeitungsvorrichtung, wobei der Speicher
einen ersten Speicherabschnitt und einen zweiten Speicherabschnitt
enthält,
der in eine Vielzahl von Blöcken
aufgeteilt ist, wobei die Blöcke
in dem zweiten Speicherabschnitt jeweils eine verknüpfte Markierung
haben, wobei der Speicher einen Computerprogramm-Code umfasst, der
ausgebildet ist zum: Durchführen
einer ersten Speicherbereinigung in dem ersten Speicherabschnitt;
Durchführen
einer zweiten Speicherbereinigung in einem ausgewählten der
Blöcke
in dem zweiten Speicherabschnitt; Durchführen einer dritten Speicherbereinigung
in dem ausgewählten
Block in dem zweiten Speicherabschnitt, wobei die dritte Speicherbereinigung
ein Bestimmen enthält,
ob der ausgewählte
Block ein erstes Objekt enthält,
das ein zweites Objekt referenziert, das nicht in dem ausgewählten Block
enthalten ist, basierend auf wenigstens teilweise einem Status,
der durch die mit dem ausgewählten
Block verknüpfte
Markierung angegeben ist, wobei der Status eine Angabe enthält, ob die Referenz
auf das zweite Objekt gespeichert wurde, nachdem die zweite Speicherbereinigung
durchgeführt
wurde; und Zuteilen eines neuen mit der Markierung verknüpften Wurzel-Arrays,
wenn die Markierung angibt, dass das zweite Objekt gespeichert wurde,
nachdem die zweite Speicherbereinigung durchgeführt wurde, wobei die Markierung
einen Zeiger auf den neuen Wurzel-Array bereitstellt, und wobei
der neue Wurzel-Array die Adresse des Zeigers auf das zweite Objekt
hält.
-
Gemäß weiteren
Aspekten der Erfindung werden ein Computerprogramm-Code, der zum
Bereitstellen der vorhergehenden Aspekte der Erfindung ausführbar ist,
und ein Computerprogramm-Produkt mit einem Computer-lesbaren Medium,
das solch einen Computerprogramm-Code trägt, bereitgestellt.
-
Dies
und andere Vorteile der vorliegenden Erfindung werden beim Lesen
der folgenden detaillierten Beschreibung und dem Studieren der vielfältigen Figuren
der Zeichnungen ersichtlich werden.
-
Die
Erfindung, zusammen mit weiteren Vorteilen davon, kann am besten
mit Verweis auf die folgende Beschreibung in Verbindung mit den
begleitenden Zeichnungen verstanden werden.
-
1 ist
diagrammartige Darstellung eines Bereiches eines Computerspeichers,
der Objekte gemäß dem Stand
der Technik enthält.
-
2 ist
eine diagrammartige Darstellung einer Schnittstelle zwischen einer
Wurzel und einem Speicher, der eine neue Generation und eine alte
Generation gemäß dem Stand
der Technik enthält.
-
3 ist
eine diagrammartige Darstellung eines Speichers, der mit einer Gedächtnismenge
gemäß dem Stand
der Technik verknüpft
ist.
-
4a ist
eine diagrammartige Darstellung eines Speichers für eine alte
Generation, der in Trains gemäß einer
Ausführungsform
der vorliegenden Erfindung segmentiert ist.
-
4b ist
eine diagrammartige Darstellung eines Arrays von Kartenmarkierungen
und eines verknüpften
Pools von Wurzel-Arrays gemäß einer
Ausführungsform
der vorliegenden Erfindung.
-
5 ist
ein Prozessflussdiagramm, das die mit einem Ausführen eines Computerprogramms
gemäß einer
Ausführungsform
der vorliegenden Erfindung verknüpften
Schritte veranschaulicht.
-
6 ist
ein Prozessflussdiagramm, das einen Prozess veranschaulicht zum
Durchführen
einer Speicherbereinigung, d.h. Schritt 510 von 5,
gemäß einer
Ausführungsform
der vorliegenden Erfindung.
-
7a ist
Prozessflussdiagramm, das die Schritte veranschaulicht, die mit
einer Speicherbereinigung in einem Speicher für eine neue Generation verknüpft sind,
d.h. Schritt 604 von 6, gemäß einer
Ausführungsform
der vorliegenden Erfindung.
-
7b ist
ein Prozessflussdiagramm, das die Schritte veranschaulicht, die
mit Folgen einer einzelnen Wurzel gemäß einer Ausführungsform
der vorliegenden Erfindung verknüpft
sind.
-
7c–7e sind
Prozessflussdiagramme, die die Schritte veranschaulichen, die mit
einem Aktualisieren einer Kartenmarkierung verknüpft sind, d.h. Schritt 728 von 7b,
gemäß einer
Ausführungsform
der vorliegenden Erfindung.
-
8a und 8b sind
Prozessflussdiagramme, die die Schritte veranschaulichen, die mit einem
Durchführen
einer Speicherbereinigung für eine
alte Generation verknüpft
sind, d.h. Schritt 608 von 6, gemäß einer
Ausführungsform
der vorliegenden Erfindung.
-
9 ist
eine diagrammartige Darstellung eines zum Implementieren der vorliegenden
Erfindung geeigneten Computersystems.
-
Detaillierte
Beschreibung der Ausführungsformen
-
Eine
Speicherbereinigung wird verwendet zum dynamischen Zuteilen eines
Computerspeichers zum Gebrauch durch ein Computerprogramm. Im Allgemeinen
verursacht eine Speicherbereinigung Pausen bei der Ausführung des
Programms. Eine Vorrichtung zum Reduzieren der mit einer Speicherbereinigung
verknüpften
Pausenzeiten nutzt ein Teilen des Speichers in eine Vielzahl von
Generationen. Mittels eines Beispiels, kann der Speicher, in einer Zweigenerations-Speicherstruktur,
in eine neue Generation aufgeteilt werden, welche neu zugeteilte
Objekte enthält,
und eine alte Generation, welche ältere Objekte enthält. Es sollte
erkannt werden, dass neu zugeteilte Objekte dazu tendieren, schneller
als ältere
Objekte zu sterben. Als solches kann eine Speicherbereinigung häufiger in
der neuen Generation als in der alten Generation durchgeführt werden. Typischerweise
ist die neue Generation relativ klein im Vergleich zu der Gesamtgröße des Speichers. Demgemäß kann eine
Speicherbereinigung innerhalb der neuen Generation typischerweise
durchgeführt
werden, ohne wesentliche Unterbrechungen während der Ausführung eines
Programmes zu verursachen.
-
Während eine
Speicherbereinigung in der neuen Generation nicht in signifikanten
Pausenzeiten resultieren kann, kann eine Speicherbereinigung in
der alten Generation signifikante Pausen während der Ausführung eines
Programmes verursachen, da die alte Generation typischerweise größer als
die neue Generation ist. Wenn Zwischengenerationszeiger von Objekten
in der alten Generation auf Objekte in der neuen Generation existieren,
werden dann die Zwischengenerationszeiger häufig verfolgt bzw. getrackt
während
ein Programm ausgeführt
wird. Die Zwischengenerationszeiger werden häufig verfolgt, da Objekte in
der alten Generation, die mit solchen Zeigern verknüpft sind,
als Wurzeln der neuen Generation betrachtet werden. Das Verfolgen
der Zeiger, oder Referenzen, von Objekten in der alten Generation
auf Objekte in der neuen Generation ist häufig kompliziert und verbraucht
deshalb Zeit.
-
Durch
Aufteilen eines Speichers für
eine alte Generation in kleinere Bereiche, oder Blöcke, können mit
einer Speicherbereinigung für
eine alte Generation verknüpfte
Pausenzeiten reduziert werden. Eine Speicherbereinigung für eine alte
Generation kann in einem spezifischen Block eines Speichers für eine alte
Generation jedes Mal durchgeführt
werden, wenn eine Speicherbereinigung für eine alte Generation durchgeführt wird.
Durch Begrenzen der Blöcke, in
denen eine Speicherbereinigung für
eine alte Generation durchgeführt
wird, und durch Speicherbereinigen in unterschiedlichen Blöcken, jedes
Mal wenn eine Speicherbereinigung für eine alte Generation durchgeführt wird,
kann die Effizienz der Speicherbereinigung für eine alte Generation gesteigert
werden.
-
Wie
zuvor erwähnt,
wird Speicher für
eine alte Generation in Blöcke
aufgeteilt, um eine Speicherbereinigung für eine alte Generation zu erleichtern.
Jedes Mal wenn eine Speicherbereinigung für eine alte Generation durchgeführt wird,
wird die Speicherbereinigung für
eine alte Generation auf irgendeinem geeigneten Block durchgeführt. Das heißt, die
Speicherbereinigung für
eine alte Generation kann inkrementell durchgeführt werden. In einer Ausführungsform
kann die Speicherbereinigung auf dem ältesten Block durchgeführt werden,
in dem eine Speicherbereinigung nicht durchgeführt worden ist. In einer anderen
Ausführungsform
kann die Speicherbereinigung auf dem Block durchgeführt werden,
in dem eine Speicherbereinigung zuletzt durchgeführt worden ist.
-
Mit
Verweis auf 4a wird die Aufteilung von Speicher
in Blöcke
innerhalb einer alten Generation gemäß einer Ausführungsform
der vorliegenden Erfindung beschrieben werden. Eine alte Generation 404 enthält Blöcke 408 eines
Speichers. Die Speicherblöcke
werden typischerweise auf einer Bedarfsbasis zugeteilt. Das heißt, die
zusätzlichen
Blöcke
werden zugeteilt, wenn im Wesentlichen sämtliche existierende Blöcke 408 voll
sind. Blöcke 408,
die mit einem bestimmten Programm verknüpfte Objekte enthalten, werden
häufig
als „Cars" bezeichnet. Blöcke oder
Cars 408 sind in verbundenen Mengen bzw. Verbundmengen
angeordnet, die als „Trains" 412 bekannt
sind. Zum Beispiel enthält
Train 412a Cars 408a, 408b, wohingegen
Train 412b Cars 408c, 408d, 408e enthält, und
Train 412c enthält
Cars 408f und 408g.
-
Cars 408 innerhalb
von Trains 412 sind im Allgemeinen basierend darauf geordnet,
wann ein bestimmtes Car 408 zu einem bestimmten Train 412 hinzugefügt wurde.
Wenn zum Beispiel innerhalb von Train 412a Car 408b zu
Train 412a nach Car 408a hinzugefügt wird,
wird Car 408a als ein niedrigeres Car in demselben Train
wie Car 408b betrachtet. Ähnlich sind Trains 412 im
Allgemeinen auch basierend darauf geordnet, wann ein bestimmter
Train 412 erschaffen wird. Wenn Train 412b zum
Speicher 404 nach Train 412c hinzugefügt wird,
wie gezeigt, ist dann Train 412 ein niedrigerer Train mit
Bezug zu Train 412b und deshalb Cars 408c, 408d, 408e innerhalb
Train 412b.
-
Ein
Algorithmus, der Trains 412 zum Durchführen einer Speicherbereinigung
innerhalb einer alten Generation 404 verwendet, sammelt
typischerweise eine Mengenanzahl von Cars 408 zu einer Zeit.
In der beschriebenen Ausführungsform
wird nur ein einzelnes Car 408 während jeder Speicherbereinigung
für eine
alte Generation gesammelt, obwohl es erkannt werden sollte, dass
irgendeine Anzahl von Cars während
jeder Speicherbereinigung für
eine alte Generation gesammelt werden kann. Durch Sammeln eines
Car 408 zu einer Zeit ist die Gesamtlänge einer Speicherbereinigung
für eine
alte Generation, und deshalb die mit einer Speicherbereinigung für eine alte
Generation verknüpften
Pausenzeiten, im Wesentlichen begrenzt. Deshalb können die
langwierigen Pausenzeiten, die typischerweise mit einer Speicherbereinigung
für eine
alte Generation verknüpft
sind, reduziert werden durch Sammeln nur einer Mengenanzahl von
Cars 408 während
jeder Speicherbereinigung für
eine alte Generation.
-
Im
Allgemeinen werden zum Durchführen
einer Speicherbereinigung in Cars 408 sämtliche Zeiger, die entweder
jedem Car 408 entstammen, oder auf Objekte innerhalb jedes
Car 408 zugreifen, im Speicher für eine alte Generation verfolgt
bzw. getrackt. Ein Verfahren zum Verfolgen von Zeigern wird unten
mit Verweis auf 4b beschrieben werden. Wenn
ein bestimmtes Car 408, welches gesammelt bzw. bereinigt
werden soll, ein lebendiges Objekt enthält, wird das Objekt in einen
Train 412 kopiert, welcher entweder auf das Objekt zeigt
oder auf welchen durch das Objekt gezeigt wird. Sobald die Kopie
des Objektes getätigt
ist, wird dann das „ursprüngliche" Objekt gesammelt
bzw. bereinigt oder zurückgefordert.
Wenn ein ausgewählter
Train 412 nicht länger Zeiger
außerhalb
von sich selbst hat, kann im Allgemeinen der ausgewählte Train 412 eliminiert
werden, d.h. der dem ausgewählten
Train 412 zugeteilte Speicher wird befreit. Wenn ein ausgewähltes Car 408 nicht
länger
Zeiger außerhalb
von sich selbst hat, kann ähnlich
das ausgewählte
Car 408 eliminiert werden.
-
In
der beschriebenen Ausführungsform
ist die Größe von Cars 408 innerhalb
eines gegebenen Train 412 feststehend. Das heißt, sämtliche
Cars 408 können
im Wesentlichen dieselbe Größe haben.
Da ein einzelnes Car 408 nicht genug Speicher zum Aufnehmen
verbundener Objekte haben kann, kann deshalb ein Objekt 416a in
einem Car 408, zum Beispiel Car 408f, mit einem
Objekt 416b in einem zweiten Car 408, zum Beispiel
Car 408g, verbunden sein. Während einer Speicherbereinigung
für eine
alte Generation überleben
Objekte 416a, 416b wahrscheinlich, da Objekte 416a, 416b in
separaten Cars 408 sind, und einander referenzieren. Als
solches kann eine Speicherbereinigung für eine alte Generation nicht
in der Sammlung bzw. Bereinigung von Cars 408f, 408g resultieren.
Deshalb wird mit Cars 408f, 408g verknüpfter Speicher
im Allgemeinen nicht befreit werden, es sei denn, Zeiger zwischen
Cars 408f, 408g innerhalb Train 412 werden
verfolgt bzw. getrackt.
-
Im
Allgemeinen kann die Länge
von Trains 412, als auch die Größe von Cars 408, breit
variiert werden. Die Größe von Cars 408 kann
von Faktoren abhängen,
die enthalten, aber nicht beschränkt
sind auf, die Größe einer
alten Generation 404, die Geschwindigkeit des Speicherbereinigungsalgorithmus und
die Geschwindigkeit des den Speicherbereinigungsalgorithmus durchführenden
Computers. Beispielhaft arbeiten Car-Größen in der Größenordnung von
ungefähr
20 Kilobytes von Speicher bis ungefähr 100 Kilobytes von Speicher
gut in aktuellen Systemen. In der beschriebenen Ausführungsform
enthalten Cars 408 ungefähr 64 Kilobytes von Speicher.
-
Cars 408 können im
Allgemeinen in Karten bzw. Cards 420 aufgeteilt sein, die
zum Erleichtern einer auf Cars 408 durchgeführten Speicherbereinigung
für eine
alte Generation verwendet werden können, wie unten mit Verweis
auf 4b beschrieben werden wird. Karten 420, ähnlich wie
Cars 408, können
irgendeine geeignete Größe annehmen,
obwohl Karten 420 häufig
ungefähr
100 bis ungefähr
1000 Bytes enthalten. Zum Beispiel können für Cars 408, die ungefähr 64 Kilobytes
von Speicher enthalten, Karten 420 ungefähr 512 Bytes
enthalten.
-
Die
Effizienz eines Verfolgens von Zwischengenerationszeigern, die von
Objekten für
eine alte Generation auf Objekte für eine neue Generation zeigen,
kann durch Bereitstellen einer Speicherung oder von Markierungen
verbessert werden, die mit unterschiedlichen Bereichen innerhalb
der alten Generation verknüpft
sind. In einer Ausführungsform
ist eine Markierung ein Wort, das ein Flag bzw. Kennzeichen enthalten
kann, das zum Angeben gesetzt ist, ob ein verknüpftes Teilstück eines
bestimmten Bereiches einen Zeiger in ein Objekt in der neuen Generation
enthält.
Die Verwendung solcher Wörter
stellt eine effiziente Struktur bereit, die verwendet werden kann
zum Verfolgen von Zwischengenerationsspeichern von Objekten in der
alten Generation auf Objekte in der neuen Generation. Obwohl beschrieben
worden ist, dass eine Markierung ein Wort ist, sollte erkannt werden,
dass eine Markierung irgendein geeigneter Speicherungstyp sein kann,
wie zum Beispiel, Bits, Bytes und ähnliches.
-
Um
Zwischengenerationszeiger von mit Karten verknüpften Objekten in Speicher
für eine
alte Generation auf Objekte in Speicher für eine neue Generation zu verfolgen,
können
Karten mit „Verfolgungsstrukturen" bereitgestellt werden.
Die Verfolgungsstrukturen, oder Kartenmarkierungen, können verwendet
werden zum Identifizieren, ob Zwischengenerationszeiger für eine Karte
existieren, und auch, welche Objekte innerhalb der Karte die Zeiger enthalten. 4b ist
eine diagrammartige Darstellung eines Arrays von Kartenmarkierungen
und eines verknüpften
Pools von Wurzel-Arrays gemäß einer Ausführungsform
der vorliegenden Erfindung. Ein Wurzel-Array ist im Allgemeinen
ein kleiner Array, der Wurzel-Orte enthält, die als Versetze relativ
zu einer Karte spezifiziert sind. Mit anderen Worten ist ein Wurzel-Array
im Wesentlichen eine kleine Gedächtnismenge.
Ein Array 450 von Kartenmarkierungen 452 enthält Zeiger 456 auf
Wurzel-Arrays 460, die in einem Pool 464 von Wurzel-Arrays 460 enthalten sind.
Kartenmarkierungen 452 sind im Allgemeinen mit einem Car
innerhalb eines Train verknüpft.
Genau genommen kann ein Car innerhalb eines Train in eine Anzahl
von Karten aufgeteilt sein, die Objekte für eine alte Generation halten.
Als solches kann ein Car innerhalb eines Train mit vielen Kartenmarkierungen 452 verknüpft sein.
Eine Gruppierung von Kartenmarkierungen 452, die sämtliche
mit einem Car verknüpfte
Kartenmarkierungen 452 enthalten können oder nicht enthalten können, wird
als ein Array 450 von Kartenmarkierungen 452 betrachtet.
In der beschriebenen Ausführungsform
enthält
Array 450 sämtliche
mit der alten Generation verknüpfte Kartenmarkierungen 452.
-
In
der beschriebenen Ausführungsform
ist eine Kartenmarkierung 452, wie zum Beispiel Kartenmarkierung 452a,
ein 32-Bit Wort, obwohl es erkannt werden sollte, das Kartenmarkierung 452 im
Allgemeinen irgendein Wort einer geeigneten Länge sein kann. Alternativ kann
die Datenstruktur, die die Kartenmarkierung ausmacht, in eine Anzahl
von Komponenten aufgeteilt sein. Obwohl die Bits, die die Kartenmarkierung 452a ausmachen,
auf irgendeine Weise zugewiesen sein können, die passend für eine bestimmte
Anwendung ist, werden in einer Ausführungsform die niedrigsten
Bits oder geringstwertigen Bits von Kartenmarkierung 452a als
Flags bzw. Kennzeichen verwendet, welche im Wesentlichen den „Status", oder die Gesamtbeschaffenheit,
von Kartenmarkierung 452a identifizieren. Ein niedrigstes Bit 468 von
Kartenmarkierung 452a ist ein „beschmutztes" Flag bzw. Kennzeichen.
Das heißt,
das niedrigste Bit 468 ist im Allgemeinen angeordnet zum Angeben,
ob Kartenmarkierung 452a, oder genauer genommen eine mit
Kartenmarkierung 452a verknüpfte Karte (wie in 4a gezeigt), „beschmutzt" ist. Beschmutzte
Karten sind Karten, wo Zeiger seit der letzten Speicherbereinigung
gespeichert worden sind. Solche Zeiger können, ohne darauf beschränkt zu sein,
einen Zwischengenerationszeiger enthalten, der vom Speicher für eine alte
Generation in Speicher für
eine neue Generation zeigt, einen Zeiger auf einen niedrigeren Train
und einen Zeiger auf ein niedrigeres Car in demselben Train, der
mit Kartenmarkierung 452a verknüpft ist. Wenn das niedrigste
Bit 468 gesetzt ist, d.h. wenn das niedrigste Bit 468 auf „0" gesetzt ist, ist
dies die Angabe, dass Kartenmarkierung 452a beschmutzt
ist.
-
Ein
zweitniedrigstes Bit 470 von Kartenmarkierung 452a ist
ein Flag für
eine neue Generation, und wird verwendet zum Angeben, ob Kartenmarkierung 452 einen
Zwischengenerationszeiger auf eine neue Generation enthält. Wenn
das zweitniedrigste Bit 470 gesetzt ist, d.h. auf „0" gesetzt ist, ist
die Angabe, dass Kartenmarkierung 452a einen Zwischengenerationszeiger
auf eine neue Generation enthält. Ein
drittniedrigstes Bit 472 ist ein Flag für einen niedrigeren Train in
der beschriebenen Ausführungsform. Das
Flag für
einen niedrigeren Train ist angeordnet zum Angeben, ob Kartenmarkierung 452a einen
Zeiger in einen niedrigeren Train hat. Wenn das Flag für einen
niedrigeren Train, oder das drittniedrigste Bit 472, gesetzt
ist, ist die Angabe, dass Kartenmarkierung 452a einen Zeiger
in einen niedrigeren Train enthält.
Ein viertniedrigstes Bit 474 ist ein Flag für einen
selben Train, das angeordnet ist zum Angeben, ob Kartenmarkierung 452a einen
Zeiger in ein niedrigeres Car in demselben Train enthält.
-
Damit
Kartenmarkierung 452a als sauber betrachtet werden kann,
kann in der beschriebenen Ausführungsform
Kartenmarkierung 452a im Allgemeinen nicht einen Zeiger
enthalten, der von einer alten Generation auf eine neue Generation
zeigt, einen Zeiger in einen niedrigeren Train oder einen Zeiger
in ein niedrigeres Car desselben Train. Als solches haben das niedrigste
Bit 468, das zweitniedrigste Bit 470, das drittniedrigste
Bit 472 und das vierniedrigste Bit 474 alle Werte
von „1" für den Fall,
dass Kartenmarkierung 452a sauber ist. Die niedrigsten
vier Bits von Kartenmarkierung 452a werden, mit anderen Worten,
gelöscht
bzw. gesäubert,
wenn Kartenmarkierung 452a sauber ist. Deshalb sind, wie
gezeigt, Kartenmarkierung 452a, 452b sauber, wohingegen Kartenmarkierung 452c,
die ein niedrigstes Bit, das gesetzt ist, beschmutzt ist.
-
Die
verbleibenden 28 Bits in Kartenmarkierung 452a können alle
zur Verwendung als ein Zeiger zugeteilt sein, der auf einen verknüpften Wurzel-Array 460 zeigt,
der im Wesentlichen Adressen von mit Kartenmarkierung 452a verknüpften Zeigern
hält. Alternativ,
wie zum Beispiel in der beschriebenen Ausführungsform, enthält Kartenmarkierung 452a einen 27-Bit
Zeiger 478 und ein reserviertes Bit 480. 27-Bit Zeiger 478 identifiziert Zeiger 456a,
der von Kartenmarkierung 452a in Wurzel-Array 460a zeigt. Wurzel-Array 460a wird
häufig
entweder von acht Wörtern
oder sechzehn Wörtern
gebildet, obwohl die Anzahl von Wörtern im Wurzel-Array 460a breit
variiert werden kann. Wurzel-Arrays 460 sind im Allgemeinen
angeordnet zum Halten von Referenzen auf Wurzeln. Beispielsweise
hält Wurzel-Array 460a mit Kartenmarkierung 452a verknüpfte Wurzeln,
und deshalb eine Karte (wie in 4a gezeigt),
die mit Kartenmarkierung 452a verknüpft ist.
-
Wenn
ein geeignetes Wurzel-Array 460 voll ist, dann wird gesagt,
dass ein „Wurzel-Array-Überlauf" auftritt. Wenn zu
viele Wurzeln in einem gegebenen Wurzel-Array 460 gespeichert
werden sollen, werden die Wurzeln nicht länger verfolgt bzw. getrackt.
Ein Wurzel-Array-Überlauf
tritt im Allgemeinen auf, wenn das Volumen von Zeigern auf Wurzel-Arrays 460 im
Pool 464 überhöht ist mit
Bezug auf die Wurzel-Arrays 460, die zur Zuteilung im Pool 464 verfügbar sind.
Beim Durchführen
einer Speicherbereinigung werden, als ein Ergebnis, die Zeiger in
der mit dem Wurzel-Array-Überlauf
verknüpften
Karte dann typischerweise zum Identifizieren von Wurzeln durchsucht.
Wenn ein Wurzel-Array-Überlauf
auftritt, wird 27-Bit Zeiger 478 auf null gesetzt. Das
heißt,
die 27 Bits im 27-Bit Zeiger 478 werden auf „0" gesetzt.
-
Das
reservierte Bit 480 kann für irgendeine Anzahl von geeigneten
Zwecken verwendet werden, welche direkt auf Speicherbereinigungszwecke
bezogen sein können
oder nicht. Geeignete Zwecke enthalten, ohne auf diese beschränkt zu sein,
ein Verwenden des reservierten Bits 480 zum Angeben des
Alters von Kartenmarkierung 452a. Es sollte erkannt werden,
dass zusätzliche
reservierte Bits auch in Kartenmarkierung 452a enthalten
sein können durch
Reduzieren der Anzahl von Bits, die mit dem Zeiger in Kartenmarkierung 452a,
zum Beispiel 27-Bit Zeiger 478, verknüpft sind.
-
Kartenmarkierungen
werden in Verbindung mit einem Speicherbereinigungsprozess zum Verfolgen
bzw. Tracken von Karten innerhalb eines Car verwendet, die Zwischengenerationszeiger
auf Objekte in einer neuen Generation enthalten. In einer Ausführungsform
können
die Kartenmarkierungen, während
der Ausführung
eines Computerprogramms, aktualisiert werden, um zu ermöglichen, dass
die Kartenmarkierungen im Wesentlichen aktuell bleiben. Dann können die
Kartenmarkierungen, während
einer Speicherbereinigung, überprüft werden
zum Identifizieren von Karten mit Zwischengenerationszeigern.
-
Wenn
Kartenmarkierungen verwendet werden, zum Erleichtern eines Speicherbereinigungsprozesses
während
der Ausführung
eines Computerprogramms, werden die Kartenmarkierungen im Allgemeinen
sowohl durchsucht als auch aktualisiert zum Identifizieren von durch
die Kartenmarkierungen referenzierten Objekten. Mit Verweis auf 5 werden
die mit einem Ausführen
eines Programms, das eine Speicherbereinigung verwendet, verknüpften Schritte
gemäß einer
Ausführungsform
der vorliegenden Erfindung beschrieben werden. Vor einem Ausführen eines
Programms wird ein Karten-Array, der mit dem Programm verknüpft ist,
in Schritt 502 initialisiert. In einer Ausführungsform
enthält
ein Karten-Array, der mit einem Car verknüpft ist, Kartenmarkierungen,
oder Referenzen auf Kartenmarkierungen, die mit durch ein Programm
verwendeten Objekten verknüpft
sind, wie oben mit Verweis auf 4b erwähnt. Als
solches wird ein Karten-Array im Allgemeinen zum Verfolgen bzw.
Tracken von in dem Programm verwendeten Objekten verwendet. Nachdem
der Karten-Array initialisiert ist, wird das Programm im Schritt 503 ausgeführt, bis
es erwünscht ist,
entweder ein Objekt zuzuteilen, oder einen Zeiger für ein Objekt
zu speichern. Ein Zuteilen eines Objektes schließt im Allgemeinen ein Zuweisen
von Speicher an ein Objekt ein, wohingegen ein Speichern eines Zeigers
für ein
Objekt ein Speichern eines Zeigers auf ein anderes Objekt innerhalb
eines ersten Objekts einschließt.
-
Während einer
Ausführung
werden periodisch Anweisungen zum Speichern eines Zeigers vorliegen.
Wenn ein Zeiger gespeichert werden soll, wie durch Schritt 504 dargestellt,
in Schritt 512, wird ein Eintrag in dem Karten-Array, der
dem Objekt entspricht, dessen Zeiger gespeichert werden soll, identifiziert.
Sobald der Karten-Array-Eintrag identifiziert ist, wird der Karten-Array-Eintrag als
beschmutzt im Schritt 514 markiert. Ein Markieren des Karten-Array-Eintrags,
d.h. der Kartenmarkierung, als beschmutzt schließt ein Setzen des Beschmutzt-Flags des
Eintrags ein, d.h. des niedrigstes Bit 468 von Eintrag 452a,
wie oben mit Verweis auf 4b beschrieben.
Nachdem die Kartenmarkierung als beschmutzt markiert ist, wird der
Zeiger im Schritt 516 mit Verwenden irgendeines geeigneten
Verfahrens gespeichert. Sobald der Zeiger geschrieben ist, kehrt
der Prozessfluss zum Schritt 502 zurück, wo das Programm mit dem
Ausführen
fortfährt.
Wenn das Programm endet, wird irgendeine erneute Ausführung des
Programms wieder bei Schritt 502 beginnen, wo ein Array
von Kartenmarkierungen initialisiert wird.
-
Eine
Ausführung
stellt im Allgemeinen auch Anweisungen zum Zuteilen eines Objektes
bereit. In einer Ausführungsform,
zurückkehrend
zum Schritt 504, wenn ein Objekt zugeteilt werden soll,
wird eine Bestimmung im Schritt 506 getätigt, ob genug Speicher für eine neue
Generation für
das zuzuteilende Objekt existiert. Wenn im Schritt 506 bestimmt
wird, dass genug Speicher für eine
neue Generation vorliegt zur Verwendung beim Zuteilen eines Objektes, dann
wird im Schritt 508 ein Objekt zugeteilt, und der Prozessfluss
kehrt zum Schritt 503 zurück, wo eine Bestimmung bezüglich dessen
getätigt
wird, ob ein zu speichernder Zeiger vorliegt.
-
Wenn
die Bestimmung im Schritt 506 derart ist, dass unzureichender
Speicher für
eine neue Generation für
ein zuzuteilendes Objekt vorliegt, dann schreitet der Prozessfluss
zum Schritt 510 fort, wo eine Speicherbereinigung durchgeführt wird.
Während
der Speicherbereinigung können
der Speicher für
eine neue Generation, und möglicherweise
der Speicher für
eine alte Generation, gelöscht
bzw. gesäubert
werden zum Freigeben von Abschnitten eines Speichers für eine neue
Generation, so dass ein Objekt zugeteilt werden kann. Obwohl erkannt
werden sollte, dass irgendein geeignetes Speicherbereinigungsverfahren
verwendet werden kann, wird unten mit Verweis auf 6 ein
bestimmtes, geeignetes Speicherbereinigungsverfahren beschrieben
werden. Nachdem die Speicherbereinigung im Schritt 510 durchgeführt ist,
kehrt der Prozessfluss zum Schritt 506 zurück, wo eine
Bestimmung bezüglich dessen
getätigt
wird, ob eine Speicherbereinigung genug Speicher für eine neue
Generation für
ein zuzuteilendes Objekt, d.h. das Objekt, dessen Zuteilung als
notwendig im Schritt 503 erachtet wurde, bereitgestellt
hat.
-
Mit
Verweis auf 6 werden die mit dem Durchführen einer
Speicherbereinigung verknüpften Schritte
gemäß einer
Ausführungsform
der vorliegenden Erfindung beschrieben werden. Das heißt, Schritt 510 von 5 wird
beschrieben werden. Eine Speicherbereinigung kann sowohl im Speicher
für eine
neue Generation als auch im Speicher für eine alte Generation durchgeführt werden
zum „Freimachen" von genug Speicherraum
zum Ermöglichen, dass
ein Objekt zugeteilt wird. Wenn das System, auf welchem eine Speicherbereinigung
durchgeführt werden
soll, ein Mehr-Thread-System ist, dann werden die Threads im Schritt 602 mit
Verwenden irgendeines geeigneten Verfahrens synchronisiert. Sobald
irgendwelche Threads, die eine Synchronisation erfordern, synchronisiert
sind, wird eine Speicherbereinigung innerhalb des Speichers für eine neue
Generation im Schritt 604 durchgeführt. Die mit einem Verfahren
zum Durchführen
solch einer Speicherbereinigung für eine neue Generation verknüpften Schritte
werden unten mit Verweis auf 7a beschrieben
werden.
-
Nachdem
die Speicherbereinigung für
eine neue Generation im Schritt 604 durchgeführt ist, schreitet
der Prozessfluss zum Schritt 606 voran, wo eine Bestimmung
hinsichtlich dessen getätigt
wird, ob es notwendig ist, eine Speicherbereinigung für Speicher
für eine
alte Generation durchzuführen.
In der beschriebenen Ausführungsform
wird eine Speicherbereinigung für
eine alte Generation periodisch durchgeführt. Das heißt, nachdem
vorbestimmte Zeitmengen verstrichen sind, wird eine Speicherbereinigung
für eine
alte Generation durchgeführt.
In anderen Ausführungsformen
können
jedoch unterschiedliche oder zusätzliche
Kriterien bei der Bestimmung hinsichtlich dessen verwendet werden,
ob eine Speicherbereinigung für
eine alte Generation benötigt
wird.
-
Wenn
bestimmt ist, dass eine Speicherbereinigung für eine alte Generation benötigt wird,
dann wird die Speicherbereinigung für eine alte Generation mit
Verwenden eines Kartenmarkierungsschemas im Schritt 608 durchgeführt. Obwohl
Verfahren, die mit einer Speicherbereinigung verknüpft sind,
die auf Speicher für
eine alte Generation durchgeführt
wird, mit Verwenden eines Kartenmarkierungsschemas weit variiert
werden können,
wird unten mit Verweis auf 8a und 8b ein
geeignetes Speicherbereinigungsverfahren für eine alte Generation beschrieben
werden.
-
Sobald
eine Speicherbereinigung für
eine alte Generation im Schritt 608 vollendet ist, ist
der Gesamtprozess zum Durchführen
einer Speicherbereinigung zum Ermöglichen, dass ein Objekt zugeteilt wird,
vollendet. Deshalb schreitet der Prozessfluss zum Schritt 506 von 5 voran,
der die Bestimmung hinsichtlich dessen ist, ob genug Speicher für eine neue
Generation verfügbar
ist für
ein zuzuteilendes Objekt. Wenn die Bestimmung im Schritt 606 derart
ist, dass keine Speicherbereinigung für eine alte Generation benötigt wird,
schreitet der Prozessfluss auch ähnlich
zum Schritt 506 von 5 voran.
-
7a ist
ein Prozessflussdiagramm, das die Schritte veranschaulicht, die
mit einer Speicherbereinigung für
eine neue Generation verknüpft
sind, d.h. Schritt 604 von 6, gemäß einer
Ausführungsform
der vorliegenden Erfindung. Im Schritt 701 wird festen
Wurzeln gefolgt. Wie zuvor erwähnt
sind feste Wurzeln Wurzeln, zum Beispiel lebende Objekte auf einem
Stapel bzw. Stack, welche direkt in den Speicher für eine neue
Generation oder den Speicher für
eine alte Generation zeigen, der durch eine Speicherbereinigung
gelöscht
bzw. gesäubert
wird. Ein Folgen fester Wurzeln erfordert typischerweise ein transitives
Folgen von Referenzen der Wurzeln. Die eigentlichen Schritte, die
mit einem Folgen von Wurzeln, festen oder anderen, verknüpft sind,
werden unten mit Verweis auf 7b–7e beschrieben
werden.
-
Nachdem
den festen Wurzeln gefolgt worden ist, wird der Karten-Array im
Schritt 702 abgesucht zum Lokalisieren des nächsten Eintrags
in dem Karten-Array mit einem Beschmutzt-Flag oder einem Flag für eine neue Generation,
das gesetzt ist, oder beiden. Wie zuvor beschrieben, bezeichnet
in einer Ausführungsform
ein „Setzen" eines Bits ein Zuweisen
eines Wertes von „0" an das Bit, wohingegen „Löschen" eines Bits auf ein
Zuweisen eines Wertes von „1" an das Bit verweist.
Im Schritt 703 wird eine Bestimmung hinsichtlich dessen
getätigt,
ob ein Eintrag, oder eine Kartenmarkierung, mit entweder einem oder
beiden eines Flags für
eine neue Generation oder eines Beschmutzt-Flags, das gesetzt ist,
gefunden worden ist. Wenn solch ein Eintrag gefunden worden ist,
schreitet dann der Prozessfluss zum Schritt 704 voran,
wo bestimmt wird, ob der Eintrag beschmutzt ist.
-
Wenn
die Bestimmung im Schritt 704 derart ist, dass der Eintrag
beschmutzt ist, dann wird die mit dem Eintrag verknüpfte Karte
als sauber im Schritt 707 markiert. Es sollte erkannt werden,
dass ein Markieren einer Karte als sauber ein Markieren der mit der
Karte verknüpften
Kartenmarkierung als sauber impliziert. Nachdem die Karte als sauber
im Schritt 707 markiert ist, schreitet der Prozessfluss
zum Schritt 708 voran, wo alle Objekte in der Karte abgesucht
werden zum Lokalisieren von Wurzeln, denen dann gefolgt wird. Wie
zuvor erwähnt,
werden die Schritte, die mit dem Folgen der Wurzeln verknüpft sind,
mit Verweis auf 7b–7e unten
erläutert werden.
Sobald den Wurzeln gefolgt worden ist, kehrt der Prozessfluss zum
Schritt 702 zurück,
wo der Karten-Array abgesucht wird zum Lokalisieren des nächsten Eintrags,
d.h. einer Kartenmarkierung, mit entweder einem oder beiden eines
Beschmutzt-Flags oder eines Flags für eine neue Generation, das
gesetzt ist.
-
Wenn
die Bestimmung im Schritt 704 derart ist, dass der Eintrag
nicht beschmutzt ist, dann hat der Eintrag ein Flag für eine neue
Generation, das gesetzt ist. Der Eintrag enthält, mit anderen Worten, einen Zwischengenerationszeiger.
Wenn der Eintrag nicht beschmutzt ist, bewegt sich der Prozessfluss vom
Schritt 704 zum Schritt 706, wo eine Bestimmung
hinsichtlich dessen getätigt
wird, ob der mit dem Eintrag verknüpfte Wurzel-Array übergelaufen ist.
Ein übergelaufener
Wurzel-Array ist ein Wurzel-Array, der nicht genug Raum hat zum
Aufnehmen der Einfügung
einer zusätzlichen
Wurzel. Wenn bestimmt ist, dass der mit dem Eintrag verknüpfte Wurzel-Array übergelaufen
ist, dann bewegt sich der Prozessfluss zum Schritt 707,
wo die mit dem Eintrag verknüpfte
Karte als sauber markiert wird.
-
Vom
Schritt 714 bewegt sich der Prozessfluss zum Schritt 718,
in dem der mit dem Eintrag verknüpfte
Block abgesucht wird, und den mit dem Block verknüpften Wurzeln
gefolgt wird. Dann kehrt der Prozessfluss zum Schritt 702 zurück, der
der Schritt zum Absuchen des Karten-Arrays ist, um nach dem nächsten Eintrag
in dem Karten-Array mit dem niedrigsten, auf null gesetzten Bit
zu suchen.
-
Wenn
ein geeigneter Eintrag, d.h. ein Eintrag mit dem niedrigsten Bit,
das auf null gesetzt ist, nicht im Schritt 703 gefunden
ist, dann wird den Wurzeln innerhalb des Speichers für eine neue
Generation gefolgt, bis sämtliche
kopierte Objekte im Schritt 710 abgesucht worden sind.
Sobald sämtliche
kopierte Objekte abgesucht worden sind, ist der Speicherbereinigungsprozess
für eine
neue Generation vollendet.
-
Im
Allgemeinen schließt
eine Speicherbereinigung ein Wurzelfolgen, wie zuvor erwähnt, ein.
Mit Verweis auf 7b werden als nächstes die
Schritte, die mit dem Folgen einer einzelnen Wurzel verknüpft sind,
gemäß einer
Ausführungsform
der vorliegenden Erfindung beschrieben werden. Der Prozess beginnt
mit einer Bestimmung im Schritt 720, ob die Wurzel in einen
Speicherbereich zeigt, in dem eine Speicherbereinigung auftritt.
Wenn bestimmt ist, dass die Wurzel nicht in einen Bereich zeigt,
der speicherbereinigt wird, dann ist der Prozess des Wurzelfolgens
vollendet. Wenn die Bestimmung in Schritt 720 derart ist,
dass die Wurzel tatsächlich
auf einen Bereich zeigt, in dem eine Speicherbereinigung auftritt,
dann wird, im Schritt 721, andererseits eine Bestimmung
hinsichtlich dessen getätigt,
ob das Header-Feld der Kartenmarkierung einen Weiterleitungszeiger
hält. Das
heißt,
eine Bestimmung wird getätigt, ob
ein neuer Speicherort durch einen mit der Kartenmarkierung verknüpften Weiterleitungszeiger
identifiziert wird.
-
Wenn
die Bestimmung ist, dass das Header-Feld einen Weiterleitungszeiger
hält, dann
bewegt sich der Prozessfluss vom Schritt 721 zum Schritt 722,
wo die Wurzel auf den neuen, durch den Weiterleitungszeiger identifizierten
Ort aktualisiert wird. Nachdem die Wurzel aktualisiert ist, wird
die Kartenmarkierung demgemäß im Schritt 728 aktualisiert.
Die mit einem Aktualisieren einer Kartenmarkierung verknüpften Schritte
werden unten mit Verweis auf 7c beschrieben
werden. Sobald die Kartenmarkierung aktualisiert ist, ist dann der
Prozess eines Wurzelfolgens vollendet.
-
Zurückkehrend
zum Schritt 721, wenn bestimmt ist, dass das Header-Feld
nicht einen Weiterleitungszeiger hält, wird dann das durch die
Wurzel identifizierte Objekt an eine neue Stelle kopiert, und ein
Weiterleitungszeiger wird in den Header des Objekts im Schritt 724 eingefügt. Der
Weiterleitungszeiger identifiziert den neuen Ort des Objekts. Wenn
der Weiterleitungszeiger in den Header eingefügt worden ist, wird die Wurzel
auf den neuen Ort im Schritt 726 aktualisiert. Dann wird,
im Schritt 728, die verknüpfte Kartenmarkierung aktualisiert,
und der Prozess des Wurzelfolgens ist vollendet.
-
Mit
Verweis auf 7c–7e werden
die mit einem Aktualisieren einer Kartenmarkierung, d.h. Schritt 728 von 7b,
verknüpften
Schritte gemäß einer
Ausführungsform
der vorliegenden Erfindung beschrieben werden. Im Schritt 740 wird
eine Bestimmung getätigt,
ob der Zeiger von der alten Generation auf die neue Generation zeigt.
Wenn der Zeiger von der alten Generation auf die neue Generation zeigt,
dann wird in Schritt 746 bestimmt, ob das Kennzeichen für eine neue
Generation gesetzt ist zum Angeben, dass ein Zeiger von der alten
Generation auf die neue Generation vorliegt.
-
Im
Schritt 752 wird eine Bestimmung getätigt, ob der Wurzel-Array übergelaufen
ist, wenn im Schritt 746 bestimmt ist, dass das Flag für eine neue Generation
gesetzt ist. Wenn der Wurzel-Array übergelaufen ist, dann ist der
Prozess des Aktualisierens einer Kartenmarkierung vollendet. Wenn
jedoch der Wurzel-Array nicht übergelaufen
ist, dann wird eine Bestimmung im Schritt 754 getätigt, ob
der Wurzel-Array voll ist. Wenn bestimmt ist, dass der Wurzel-Array
nicht voll ist, schreitet der Prozessfluss zum Schritt 750 voran,
in dem die Wurzel, d.h. die Wurzel, der gefolgt wird, in den Wurzel-Array
eingefügt
wird, und der Prozess des Aktualisierens einer Kartenmarkierung
ist vollendet. Wenn im Schritt 754 bestimmt ist, dass der
Wurzel-Array voll ist, bewegt sich dann der Prozessfluss zum Schritt 755,
wo die Kartenmarkierung gesetzt wird zum Angeben, dass ein Überlauf des
Wurzel-Arrays vorliegt. Nachdem die Kartenmarkierung gesetzt ist,
ist der Kartenmarkierungs-Aktualisierungsprozess vollendet.
-
Zurückkehrend
zum Schritt 746 und der Bestimmung, ob das Flag für eine neue
Generation gesetzt ist, wenn bestimmt ist, dass der Zeiger für eine neue
Generation nicht gesetzt ist, wird dann im Schritt 747 der
Zeiger für
eine neue Generation gesetzt zum Angeben, dass der Zeiger von der
alten Generation auf die neue Generation zeigt. Ein Wurzel-Array
wird zugeteilt und die Kartenmarkierung wird gesetzt zum Zeigen
auf den zugeteilten Wurzel-Array im Schritt 748. Vom Schritt 748 schreitet
der Prozessfluss zum Schritt 749 voran, der die Bestimmung
ist, ob die Zuteilung des Wurzel-Arrays erfolgreich war. Wenn die
Zuteilung des Wurzel-Arrays erfolgreich war, wird dann die gefolgte
Wurzel in den Wurzel-Array im Schritt 750 eingefügt. Wenn
die Zuteilung des Wurzel-Arrays nicht erfolgreich war, schreitet
dann der Prozessfluss jedoch zum Schritt 755 voran, wo
die Kartenmarkierung gesetzt wird zum Angeben, dass der Wurzel-Array übergelaufen ist.
In einer Ausführungsform
schließt
ein Setzen der Kartenmarkierung zum Angeben, dass der Wurzel-Array übergelaufen
ist, ein Setzen des Zeigers, d.h. des 27-Bit Zeigers, in der Kartenmarkierung
auf null ein.
-
Wenn
die Bestimmung im Schritt 740 derart war, dass der Zeiger
nicht von der alten Generation auf die neue Generation zeigt, schreitet
dann der Prozessfluss zum Schritt 742 voran, der die Bestimmung
ist, ob ein Zeiger vorliegt, der auf einen niedrigeren Train in
der alten Generation zeigt. Wenn bestimmt ist, dass der Zeiger auf
einen niedrigeren Train in der alten Generation zeigt, wird dann
eine Bestimmung im Schritt 760 getätigt, ob das Flag für eine neue
Generation gesetzt ist zum Angeben, dass ein Zeiger von der alten
Generation auf die neue Generation vorliegt. Wenn die Angabe derart
ist, dass das Flag für
eine neue Generation gesetzt ist, schreitet der Prozessfluss dann
zum Schritt 775 voran, der die Bestimmung ist, ob das Flag
für einen
niedrigeren Train gesetzt ist. In der beschriebenen Ausführungsform
ist das Kennzeichen für
einen niedrigeren Train das drittniedrigste Bit in der Kartenmarkierung
und gibt an, ob der Zeiger in einen niedrigeren Train zeigt, wie
oben beschrieben. Wenn die Bestimmung derart ist, dass das Flag
für einen
niedrigeren Train gesetzt ist, ist der Prozess eines Aktualisierens
der Kartenmarkierung vollendet. Wenn die Bestimmung derart ist,
dass das Flag für
einen niedrigeren Train nicht gesetzt ist, wird dann alternativ
das Flag für
einen niedrigeren Train im Schritt 776 gesetzt. Sobald
das Überlaufflag
für einen
niedrigeren Train gesetzt ist, ist dann der Kartenmarkierungs-Aktualisierungsprozess vollendet.
-
Wenn
im Schritt 760 bestimmt ist, dass das Flag für eine neue
Generation nicht gesetzt ist, bewegt sich dann der Prozessfluss
zum Schritt 766, wo bestimmt wird, ob das Flag für einen
niedrigeren Train gesetzt ist. Wenn die Bestimmung im Schritt 766 derart
ist, dass das Flag für
einen niedrigeren Train nicht gesetzt ist, wird dann das Flag für einen niedrigeren
Train im Schritt 767 gesetzt zum Angeben der Existenz eines
Zeigers auf einen niedrigeren Train. Sobald das Kennzeichen für einen
niedrigeren Train gesetzt ist, wird ein Wurzel-Array zugeteilt und die
Kartenmarkierung wird gesetzt zum Zeigen auf den zugeteilten Wurzel-Array
im Schritt 768. Eine Bestimmung wird im Schritt 769 hinsichtlich
dessen getätigt,
ob die Zuteilung des neuen Wurzel-Arrays erfolgreich war. Wenn die
Zuteilung erfolgreich war, wird dann im Schritt 770 die
gefolgte Wurzel in den Wurzel-Array eingefügt, für den Fall, dass die Wurzel nicht
schon bereits in dem Wurzel-Array vorhanden ist. Dann ist der Prozess
eines Aktualisierens der Kartenmarkierung vollendet. Andererseits,
wenn die Zuteilung eines neuen Wurzel-Arrays im Schritt 769 nicht
erfolgreich war, bewegt sich dann der Prozessfluss vom Schritt 769 zum
Schritt 762, wo die Kartenmarkierung gesetzt wird zum Angeben
eines Wurzel-Array-Überlaufs.
-
Nachdem
die Kartenmarkierung gesetzt ist zum Angeben eines Wurzel-Array-Überlaufs,
ist der Prozess eines Aktualisierens der Kartenmarkierung vollendet.
-
Zurückkehrend
zum Schritt 766, wenn die Bestimmung derart ist, dass das
Flag für
einen niedrigeren Train gesetzt ist zum Angeben, dass ein Zeiger
auf einen niedrigeren Train vorliegt, wird dann eine Bestimmung
im Schritt 772 getätigt,
ob der entsprechende Wurzel-Array übergelaufen ist. Wenn der Wurzel-Array übergelaufen
ist, dann wird die Kartenmarkierung gesetzt zum Angeben eines Wurzel-Array-Überlaufs
im Schritt 762. Alternativ, wenn im Schritt 772 bestimmt
ist, dass der Wurzel-Array nicht übergelaufen ist, dann wird
die gefolgte Wurzel in den Wurzel-Array im Schritt 770 eingefügt, wenn
die Wurzel nicht schon bereits in dem Wurzel-Array vorhanden ist.
-
Mit
Rückverweis
auf Schritt 742, welcher der Schritt zum Bestimmen ist,
ob ein Zeiger auf einen niedrigeren Train in der alten Generation
zeigt, wenn bestimmt ist, dass ein Zeiger nicht auf einen niedrigeren
Train in der alten Generation zeigt, wird dann im Schritt 744 eine
Bestimmung getätigt,
ob ein Zeiger auf ein niedrigeres Car in demselben Train zeigt. Wenn
ein Zeiger nicht auf ein niedrigeres Car in demselben Train zeigt,
ist dann der Kartenmarkierungs-Aktualisierungsprozess vollendet.
Wenn jedoch die Bestimmung derart ist, dass ein Zeiger auf ein niedrigeres
Car in demselben Train zeigt, schreitet dann der Prozessfluss zum
Schritt 780 voran, der die Bestimmung ist, ob das Kennzeichen
für eine neue
Generation gesetzt ist zum Angeben, dass ein Zeiger auf eine neue
Generation vorliegt.
-
Wenn
die Angabe im Schritt 780 derart ist, dass das Kennzeichen
für eine
neue Generation gesetzt ist, dann wird im Schritt 782 eine
Bestimmung getätigt,
ob das Flag für
einen selben Train gesetzt ist. Das heißt, eine Bestimmung wird getätigt, ob
ein Zeiger in ein niedrigeres Car desselben Train vorliegt. Wenn
das Flag für
einen selben Train gesetzt ist, dann ist der Prozess eines Aktualisierens
einer Kartenmarkierung vollendet. Alternativ, wenn das Flag für einen
selben Train nicht gesetzt ist, dann wird das Flag für einen
selben Train im Schritt 783 gesetzt. Sobald das Flag für einen
selben Train gesetzt ist, ist dann der Prozess eines Aktualisierens
einer Kartenmarkierung vollendet.
-
Wenn
im Schritt 780 bestimmt ist, dass das Flag für eine neue
Generation nicht gesetzt ist, dann schreitet der Prozessfluss zum
Schritt 786 voran, wo eine Bestimmung getätigt wird,
ob das Flag für
einen niedrigeren Train der Kartenmarkierung gesetzt ist zum Angeben,
dass ein Zeiger auf einen niedrigeren Train vorliegt. Wenn das Flag
für einen
niedrigeren Train gesetzt ist, schreitet dann der Prozessfluss zum Schritt 782 voran,
wo eine Bestimmung getätigt
wird, ob das Flag für
einen selben Train gesetzt ist.
-
Wenn
das Flag für
einen niedrigeren Train nicht gesetzt ist oder angibt, dass Zeiger
auf einen niedrigeren Train nicht existent sind, dann bewegt sich
der Prozessfluss vom Schritt 786 zum Schritt 788,
wo eine Bestimmung getätigt
wird, ob das Flag für
einen selben Train der Kartenmarkierung gesetzt ist. Schritt 788 ist,
mit anderen Worten, die Bestimmung, ob die Kartenmarkierung angibt,
dass ein Zeiger auf eine niedrigere Karte desselben Trains vorliegt.
Wenn bestimmt ist, dass das Flag für einen selben Train gesetzt
ist, dann wird in Schritt 790 bestimmt, ob ein mit der
Kartenmarkierung verknüpfter Wurzel-Array übergelaufen
ist. Wenn der Wurzel-Array übergelaufen
ist, dann wird die Kartenmarkierung gesetzt zum Angeben, dass der
Wurzel-Array übergelaufen
ist, im Schritt 793. Alternativ, wenn bestimmt ist, dass
der Wurzel-Array nicht übergelaufen
ist, dann wird im Schritt 792 die Wurzel in den Wurzel-Array eingefügt, für den Fall,
dass die Wurzel nicht schon bereits in dem Wurzel-Array vorhanden
ist. Nachdem die Wurzel in den Block-Array eingefügt ist, wenn
erforderlich, ist der Prozess eines Aktualisierens einer Kartenmarkierung
vollendet.
-
Zurückkehrend
zum Schritt 788, wenn bestimmt ist, dass das Flag für einen
selben Train nicht gesetzt ist, wird dann ein Wurzel-Array im Schritt 794 zugeteilt.
Ferner wird die Kartenmarkierung gesetzt zum Zeigen auf den zugeteilten
Wurzel-Array. Im Schritt 796 wird bestimmt, ob die Zuteilung
des Wurzel-Arrays erfolgreich war. Wenn die Zuteilung des Wurzel-Arrays
nicht erfolgreich war, wird dann die Kartenmarkierung gesetzt zum
Angeben eines Wurzel-Array-Überlaufs
im Schritt 782. Anderenfalls, wenn die Zuteilung des Wurzel-Arrays
erfolgreich war, schreitet dann der Prozessfluss zum Schritt 792 voran,
wo die Wurzel in den Wurzel-Array eingefügt wird, wenn die Wurzel nicht
schon bereits in dem Wurzel-Array vorhanden ist.
-
8a und 8b sind
Prozessflussdiagramme, die die Schritte veranschaulichen, die mit einem
Durchführen
einer Speicherbereinigung in einem Speicher für eine alte Generation verknüpft sind, d.h.
Schritt 608 von 6, gemäß einer Ausführungsform
der vorliegenden Form. Im Schritt 802 wird festen Wurzeln
oder, in dieser Ausführungsform, Wurzeln,
die direkt in einen Speicher für
eine alte Generation zeigen, gefolgt. Diese mit dem Folgen der festen
Wurzeln verknüpften
Schritte, gemäß einer Ausführungsform
der vorliegenden Erfindung, wurden mit Bezug zu 7b–7e oben
beschrieben.
-
Wurzeln
von der neuen Generation wird im Schritt 804 gefolgt. Das
heißt,
den Verknüpfungen von
Wurzeln, die von der neuen Generation zu der alten Generation zeigen,
wird gefolgt. Der Prozessfluss bewegt sich vom Schritt 804 zum
Schritt 806, in dem der Karten-Array oder Array von Kartenmarkierungen abgesucht
wird, um den nächsten
Eintrag zu lokalisieren, der ein Flag für einen niedrigeren Train hat, das
gesetzt ist. Im Schritt 808 wird bestimmt, ob solch ein
Eintrag, d.h. ein Eintrag, der ein Flag für einen niedrigeren Train hat,
das gesetzt ist, gefunden ist. Wenn bestimmt ist, dass solch ein
Eintrag gefunden worden ist, dann wird im Schritt 812 eine
Bestimmung getätigt,
ob der Eintrag, der gefunden wurde, eines oder beides eines Flags
für eine
neue Generation ist, das gesetzt ist, oder einen Wurzel-Array hat, der übergelaufen
ist. Wenn das Flag für
eine neue Generation gesetzt ist und/oder ein Wurzel-Array übergelaufen
ist, dann werden im Schritt 814 sämtliche Objekte in der Karte
nach Wurzeln abgesucht, und den Wurzeln wird gefolgt. Wiederum wurden
die mit einem Wurzelfolgen verknüpften
Schritte zuvor mit Bezug zu 7b–7e erläutert. Nachdem den
Wurzeln gefolgt ist, kehrt der Prozessfluss zum Schritt 806 zurück, wo der
Karten-Array abgesucht wird zum Lokalisieren des nächsten Eintrags,
der ein Flag für
einen niedrigeren Train hat, das gesetzt ist.
-
Wenn
im Schritt 812 bestimmt ist, dass der Eintrag, d.h. die
Kartenmarkierung, der im Schritt 808 gefunden wurde, nicht
ein Flag für
eine neue Generation hat, das gesetzt ist, oder ein Wurzel-Array,
der übergelaufen
ist, bewegt sich dann der Prozessfluss zum Schritt 816,
wo die mit dem Eintrag verknüpfte Wurzel
abgesucht wird, und den mit dem Wurzel-Array verknüpften Wurzeln
gefolgt wird. Nachdem den Wurzel gefolgt ist, wird der Karten-Array
im Schritt 806 nach dem nächsten Eintrag abgesucht, der
ein Flag für
einen niedrigeren Train hat, das gesetzt ist.
-
Zurückkehrend
zum Schritt 808, wenn ein Absuchen des Karten-Arrays mit
Suchen nach einem Eintrag, der ein Flag für einen niedrigeren Train hat, das
gesetzt ist, darin resultiert, dass kein Eintrag gefunden wird,
dann wird im Schritt 820 den Wurzeln innerhalb der alten
Generation gefolgt, bis sämtliche kopierte
Objekte abgesucht worden sind. In einer Ausführungsform werden Objekte in
einem Train, welche von anderen Trains referenziert werden, in diese
Trains kopiert. Wenn sämtliche
kopierte Objekte abgesucht worden sind, wird eine Bestimmung getätigt im
Schritt 830, ob einer Wurzel, die in den niedrigsten Train
zeigt, während
des Absuchprozesses begegnet wurde. Wenn die Bestimmung derart ist, dass
einer Wurzel in den niedrigsten Train nicht begegnet wurde, dann
wird im Schritt 840 der gesamte niedrigste Train freigegeben.
Es wird, mit anderen Worten, der durch den niedrigsten Train gehaltene Speicher
für eine
alte Generation befreit.
-
Der
gesamte niedrigste Train kann aufgrund der Tatsache befreit werden,
dass, wenn keine Zeiger in den niedrigsten Train existieren, der
niedrigste Train nur Abfall enthält.
Es sollte erkannt werden, dass zyklische Strukturen, die einander
referenzieren und sich in unterschiedlichen Cars desselben Trains befinden,
gesammelt bzw. bereinigt werden können, wenn der gesamte niedrigste
Train befreit wird. Sobald der gesamte niedrigste Train befreit
ist, sind die mit einer Speicherbereinigung für eine alte Generation verknüpften Schritte
vollendet.
-
Wenn
im Schritt 830 bestimmt ist, dass einer Wurzel in den niedrigsten
Train begegnet wurde, wird dann im Schritt 832 der Karten-Array
abgesucht zum Suchen nach dem nächsten
Eintrag, d.h. der Kartenmarkierung, der ein Flag für einen
selben Train hat, das gesetzt ist. Im Schritt 834 wird
eine Bestimmung getätigt,
ob ein Eintrag mit einem Flag für
einen selben Train, das gesetzt ist, gefunden worden ist. Wenn die
Bestimmung derart ist, dass ein Eintrag mit einem Flag für einen
selben Train, das gesetzt ist, nicht gefunden worden ist, wird dann
den Wurzeln innerhalb der alten Generation gefolgt, bis sämtliche kopierte
Objekte im Schritt 836 abgesucht worden sind. Ein Absuchen
sämtlicher
kopierter Objekte schließt,
in einer Ausführungsform,
eine transitive Suche nach Zeigern ein. Sobald sämtliche kopierte Objekte abgesucht
worden sind, wird das niedrigste Car in dem niedrigsten Train im
Schritt 837 freigegeben, und ein Speicherbereinigungsprozess
für eine alte
Generation ist vollendet.
-
Zurückkehrend
zum Schritt 834, wenn die Bestimmung derart ist, dass ein
Eintrag, oder eine Kartenmarkierung, mit einem Flag für einen
selben Train, das gesetzt ist, gefunden worden ist, schreitet dann
der Prozessfluss zum Schritt 838 voran, wo eine Bestimmung
getätigt
wird, ob der Eintrag ein Flag für
eine neue Generation hat, das gesetzt ist, ein Flag für einen
niedrigeren Train, das gesetzt ist, oder einen Wurzel-Array, der übergelaufen
ist. Wenn der Eintrag irgendeines oder sämtliche eines Flags für eine neue
Generation hat, das gesetzt ist, dann werden im Schritt 850 sämtliche
Objekte in der Karte nach Wurzeln abgesucht, denen gefolgt wird.
Sobald den Wurzeln gefolgt ist, kehrt der Prozessfluss zum Schritt 832 zurück, wo der
Karten-Array noch einmal abgesucht wird zum Suchen nach dem nächsten Eintrag,
der ein Flag für
einen selben Train hat, das gesetzt ist.
-
Wenn
die Bestimmung im Schritt 838 derart ist, dass der im Schritt 834 gefundene
Eintrag nicht ein Flag für
eine neue Generation enthält,
das gesetzt ist, ein Flag für
einen niedrigeren Train, das gesetzt ist oder einen Wurzel-Array,
der übergelaufen ist,
dann wird der im Eintrag verknüpfte
Wurzel-Array im Schritt 856 abgesucht, und den Wurzeln
in dem Wurzel-Array wird gefolgt. Nachdem den Wurzeln gefolgt ist,
kehrt der Prozessfluss zum Schritt 832 zurück, wo der
Karten-Array abgesucht wird zum Suchen nach dem nächsten Eintrag,
der ein Flag für
einen selben Train hat, das gesetzt ist.
-
Es
sollte erkannt werden, dass ein Computerprogramm, das eine Zwischengenerations-Speicherbereinigung
gemäß der vorliegenden Erfindung
verwendet, auf einer Vielfalt unterschiedlicher Computersysteme
implementiert werden kann. 9 veranschaulicht
ein typisches Allzweckcomputersystem, das geeignet ist zum Implementieren
der vorliegenden Erfindung. Das Computersystem 930 enthält eine
Anzahl von Prozessoren 932 (auch als Zentralverarbeitungseinheiten,
oder CPUs bezeichnet), die mit Speichergeräten gekoppelt sind mit Primärspeicherungsgeräten 934 (typischerweise
einem Nurlesespeicher, oder ROM) und Primärspeicherungsgeräten 936 (typischerweise
einem Direktzugriffsspeicher, oder RAM). Es ist in dem Fachgebiet auch
bekannt, dass ein ROM zum Transferieren von Daten und Anweisungen
auf unidirektionale Weise an die CPU 932 agiert ist, wohingegen
das RAM typischerweise verwendet wird zum Transferieren von Daten
und Anweisungen auf bidirektionale Weise. Beide Primärspeicherungsgeräte 934, 936 können irgendwelche
geeigneten Computer-lesbaren
Medien enthalten. Ein Sekundärspeicherungsmedium 938, das
typischerweise ein Massenspeichergerät ist, ist auch bidirektional
an die CPU 932 gekoppelt und stellt eine zusätzliche
Datenspeicherungskapazität bereit.
Das Massenspeichergerät 938 ist
ein Computer-lesbares Medium, das zum Speichern von Programmen mit
Computer-Code, Daten
und Ähnlichem verwendet
werden kann, und typischerweise ein Speicherungsmedium ist, so wie
eine Festplatte oder ein Band, die im Allgemeinen langsamer sind
als Primärspeicherungsgeräte 934, 936.
Massenspeicher-Speicherungsgerät 938 kann
die Form eines Magnet- oder Papierbandlesegerätes oder irgendeines anderen
wohl bekannten Gerätes
annehmen. Es wird erkannt werden, dass die innerhalb des Massenspeichergeräts 938 festgehaltene
Information, in angemessenen Fällen,
auf Standardweise als Teil vom RAM 936 als virtueller Speicher
aufgenommen sein kann. Ein spezifisches Primärspeicherungsgerät 934,
so wie eine CD-ROM, kann auch Daten unidirektional an die CPU weiterreichen.
-
CPU 932 ist
auch mit einem oder mehreren Eingabe-/Ausgabegeräten 940 gekoppelt,
die Geräte so
wie Videomonitore, Track Balls, Mäuse, Tastaturen, Mikrophone,
tastsensitive Anzeigen, Umwandler-Kartenlesegeräte, Magnet- oder Papierbandlesegeräte, Schreibtabletts,
Stifte, Sprach- oder Handschrifterkennungsgeräte oder andere wohl bekannte Eingabegeräte, so wie
selbstverständlich
andere Computer, enthalten können,
aber nicht auf diese beschränkt
sind. Schließlich
kann CPU 932 optional mit einem Computer- oder Telekommunikationsnetzwerk gekoppelt
sein, zum Beispiel einem Internet-Netzwerk oder einem Intranet-Netzwerk,
mit Verwenden einer Netzwerkverbindung wie allgemein bei 912 gezeigt.
Mit solch einer Netzwerkverbindung wird ins Auge gefasst, dass die
CPU 932 Information von dem Netzwerk empfangen könnte, oder
Information an das Netzwerk ausgeben könnte, in dem Verlauf des Durchführens der
oben beschriebenen Verfahrensschritte. Solch eine Information, die
häufig
als eine Sequenz von mit Verwenden von CPU 932 auszuführenden
Anweisungen dargestellt wird, kann von dem Netzwerk empfangen werden
und an dieses ausgegeben werden in der Form eines in einer Trägerwelle
aufgenommenen Computerdatensignals. Die oben beschriebenen Geräte und Materialien
werden dem Fachmann auf dem Gebiet der Computerhardware und Software
bekannt sein.
-
Obwohl
nur wenige Ausführungsformen
der vorliegenden Erfindung beschrieben worden sind, sollte verstanden
werden, dass die vorliegende Erfindung in vielen anderen spezifischen
Formen verkörpert
sein kann. Zum Beispiel können
Schritte umgeordnet werden, die sich mit einer Speicherbereinigung
für eine
neue Generation und die Speicherbereinigung für eine alte Generation beschäftigen. Schritte
können
auch entfernt oder hinzugefügt
werden.
-
Während eine
Generationsspeicherbereinigung derart beschrieben worden ist, dass
sie in einer neuen Generation und einer alten Generation eines Speichers
durchgeführt
wird, kann eine Generationsspeicherbereinigung über viele Generationen eines Speichers
durchgeführt
werden. Das heißt,
Generationsspeicherbereinigungsverfahren der vorliegenden Erfindung
können
auf einem Speicher durchgeführt werden,
der in vielfache Generationen aufgeteilt ist, zum Beispiel eine
neue Generation, eine „Zwischengeneration" und eine alte Generation,
die durch das Alter der Objekte innerhalb jeder Generation abgespalten
sind. Es sollte erkannt werden, dass für Ausführungsformen, in denen es mehrfache
Generationen gibt, ein Zwischengenerationszeiger im Allgemeinen
zwischen irgendwelche zwei Generationen zeigen kann.
-
Die
Kartenmarkierung ist als ein 32-Bit Wort beschrieben worden, das
einen 27-Bit Zeiger, ein reserviertes Bit, ein Flag für einen
selben Train, ein Flag für
einen niedrigeren Train, ein Flag für eine neue Generation, und
ein Beschmutzt-Flag enthält. Jedoch
sollte erkannt werden, dass die Kartenmarkierung variiert werden
kann. Zum Beispiel kann die Karten-Markierung weniger als 32 Bits
oder mehr als 32 Bits enthalten, abhängig wenigstens teilweise von bestimmten
Erfordernissen eines gegebenen Systems. Alternativ können die
Bits innerhalb einer Kartenmarkierung auch weit variiert werden.
Beispielsweise können
in einer Ausführungsform
die 32-Bits innerhalb
einer Kartenmarkierung einen 26-Bit Zeiger und zwei „Status-Indikator"-Bits oder Anhänger-Bits enthalten. Die
Status-Indikator-Bits können
angeordnet sein, so dass unterschiedliche Kombinationen von Werten
für die
Status-Indikator-Bits angeben, ob eine Kartenmarkierung beschmutzt,
sauber ist, einen Zeiger für
eine neue Generation enthält
oder nicht einen Zeiger für
eine neue Generation enthält.
Die Kartenmarkierung kann auch ein Flag für einen niedrigeren Train enthalten,
das anzeigt, ob es irgendwelche Zeiger auf einen niedrigeren Train
gibt, ein Flag für
einen selben Train, das angibt, ob es irgendwelche Zeiger innerhalb
eines gegebenen Trains auf ein niedrigeres Car in diesem Train gibt,
ein Überlauf-Flag für einen
niedrigeren Train und ein Überlauf-Flag
für einen
selben Train.
-
Obwohl
die niedrigsten Bits innerhalb einer Kartenmarkierung als Flags
beschrieben worden sind, die verwendet werden zum Angeben, ob beispielsweise
die Kartenmarkierung beschmutzt ist, wie oben beschrieben, sollte
ferner erkannt werden, dass die Flags innerhalb einer Kartenmarkierung
sich an irgendeiner geeigneten Position innerhalb der Kartenmarkierung
befinden können.
Die Bits, die Flags darstellen, können die höchsten Bits innerhalb einer
Kartenmarkierung sein. Alternativ können die Bits durch eine Kartenmarkierung
hinweg eingestreut sein.
-
Obwohl
die Größe eines
Car in einem Speicher für
eine alte Generation als feststehend beschrieben worden ist, sollte
erkannt werden, dass, in einer Ausführungsform, die Größe eines
Car dynamisch zugeteilt werden kann. Das heißt, die Größe eines Car kann basierend
auf den bestimmten Erfordernissen von Objekten bestimmt werden,
die innerhalb des Car platziert werden. Beispielsweise kann die
Größe eines
Car während
des Verlaufs eines Speicherbereinigungsprozesses zugeteilt werden, um
die Anzahl von Zeigern zu minimieren, die Objekten innerhalb des
Car entstammen.
-
Es
sollte erkannt werden, dass konventionelle Verfahren zur Speicherbereinigung
gelegentlich innerhalb eines Speichers für eine alte Generation implementiert
werden können.
Beispielsweise ist es in manchen Fällen für einen Speicher für eine alte
Generation möglich,
voll zu werden. Wenn ein Speicher für eine alte Generation voll
ist, kann ein Durchführen einer
Speicherbereinigung für
eine alte Generation auf einem Car nicht eine ausreichende Speichermenge
zur unmittelbaren Verwendung befreien. Als solches kann ein konventioneller
Speicherbereinigungsprozess, so wie ein Markierungssäuberungsbereinigungsprozess,
implementiert werden, um so viel Speicher wie möglich für eine alte Generation für den Fall
zu befreien, dass der Speicher für
eine alte Generation voll ist.