Automatische Speicherverwaltung

Aktualisiert: November 2007

Bei der automatischen Speicherverwaltung handelt es sich um einen Dienst, der von der Common Language Runtime während der verwalteten Ausführung zur Verfügung gestellt wird. Der Garbage Collector der Common Language Runtime verwaltet für eine Anwendung die Reservierung und Freigabe von Arbeitsspeicher. Das bedeutet, dass beim Entwickeln verwalteter Anwendungen kein Code für Aufgaben der Speicherverwaltung geschrieben werden muss. Mithilfe der automatischen Speicherverwaltung können häufig auftretende Probleme beseitigt werden, z. B. wenn ein Objekt versehentlich nicht freigegeben wurde und dadurch Speicherverluste entstehen oder wenn auf den Speicher für ein Objekt zugegriffen wird, das bereits freigegeben wurde. In diesem Abschnitt wird beschrieben, wie Sie den Garbage Collector zum Reservieren und Freigeben von Arbeitsspeicher verwenden können.

Reservieren von Speicherplatz

Wenn Sie einen neuen Prozess initialisieren, wird diesem durch die Common Language Runtime ein zusammenhängender Adressraum reserviert. Dieser reservierte Adressraum wird als verwalteter Heap bezeichnet. Im verwalteten Heap steht ein Zeiger zur Verfügung, der auf die Adresse des nächsten im Heap zu speichernden Objekts zeigt. Anfangs ist dieser Zeiger auf die Basisadresse des verwalteten Heaps eingestellt. Dem verwalteten Heap werden alle Referenztypen zugewiesen. Wurde von einer Anwendung der erste Referenztyp erstellt, wird diesem Typ an der Basisadresse des verwalteten Heaps Speicherplatz reserviert. Wenn das nächste Objekt von der Anwendung erstellt wird, reserviert der Garbage Collector Speicherplatz im Adressraum direkt hinter dem ersten Objekt. Solange ein Adressbereich verfügbar ist, fährt der Garbage Collector auf diese Weise mit der Reservierung von Arbeitsspeicher für neue Objekte fort.

Das Reservieren von Speicher im verwalteten Heap beansprucht weniger Zeit als die nicht verwaltete Speicherreservierung. Da durch die Common Language Runtime Speicher für ein Objekt durch Hinzufügen eines Werts zu einem Zeiger reserviert wird, ist diese Methode fast so schnell wie das Reservieren von Speicher im Stapel. Außerdem werden neue Objekte, für die Speicher reserviert wird, ständig nacheinander im verwalteten Heap gespeichert. Dadurch kann eine Anwendung auf diese Objekte sehr schnell zugreifen.

Freigeben von Arbeitsspeicher

Durch das Optimierungsmodul des Garbage Collectors wird der beste Zeitpunkt für das Ausführen einer Garbage Collection bestimmt, die auf den erfolgten Reservierungen basiert. Beim Ausführen einer Garbage Collection wird Speicher freigegeben, den von der Anwendung nicht mehr benötigte Objekte beanspruchen. Durch Überprüfen der Stammelemente der Anwendung wird ermittelt, welche Objekte nicht mehr verwendet werden. Jede Anwendung verfügt über einen Satz von Stammelementen. Jedes Stammelement bezieht sich entweder auf ein Objekt im verwalteten Heap oder ist auf NULL festgelegt. Die Stammelemente einer Anwendung beinhalten globale und statische Objektzeiger, lokale Variablen und Referenzobjektparameter im Stapel des Threads sowie CPU-Register. Der Garbage Collector hat Zugriff auf eine Liste der aktiven Stammelemente, die vom JIT (Just-In-Time)-Compiler und der Common Language Runtime verwaltet wird. Mithilfe dieser Liste werden die Stammelemente einer Anwendung durchsucht, und es wird ein Diagramm erstellt, in dem alle von den Stammelementen aus erreichbaren Objekte enthalten sind.

Objekte, die nicht in diesem Diagramm aufgeführt werden, können von den Stammelementen aus nicht erreicht werden. Diese nicht erreichbaren Objekte werden vom Garbage Collector als Abfall betrachtet, und der von diesen Objekten belegte Speicherplatz wird wieder freigegeben. Während einer Garbage Collection wird der verwaltete Heap nach den Blöcken des Adressraums durchsucht, in denen sich nicht erreichbare Objekte befinden. Beim Auffinden dieser Objekte werden die erreichbaren Objekte mithilfe einer Speicherkopierfunktion im Speicher komprimiert, und die von den nicht erreichbaren Objekten belegten Blöcke des Adressraums werden freigegeben. Nach dem Komprimieren des Speichers für die erreichbaren Objekte werden vom Garbage Collector die erforderlichen Korrekturen am Zeiger vorgenommen, sodass die Stammelemente der Anwendung auf die neuen Speicherorte der Objekte verweisen. Außerdem wird der Zeiger des verwalteten Heaps auf die Position hinter dem letzten erreichbaren Objekt gesetzt. Beachten Sie, dass der Speicher nur dann komprimiert wird, wenn in der Garbage Collection eine signifikante Anzahl nicht erreichbarer Objekte gefunden wird. Wenn nach einer Garbage Collection alle Objekte in einem verwalteten Heap verbleiben, bedarf es auch keiner Speicherkomprimierung.

Um die Leistung zu verbessern, wird großen Objekten durch die Common Language Runtime Speicherplatz in einem separaten Heap zugewiesen. Der Speicherplatz für große Objekte wird durch den Garbage Collector automatisch freigegeben. Um das Verschieben großer Objekte im Speicher zu vermeiden, wird dieser Speicher jedoch nicht komprimiert.

Generationen und Leistungsfähigkeit

Zur Optimierung der Leistung des Garbage Collectors wird der verwaltete Heap in die drei Generationsstufen 0, 1 und 2 unterteilt. Der Garbage Collection-Algorithmus der Common Language Runtime basiert auf mehreren Annahmen, die sich in der Softwarebranche anhand von Experimenten mit verschiedenen Entwürfen von Garbage Collections bestätigen ließen. Erstens kann die Garbage Collection schneller ausgeführt werden, wenn der Speicher nur in einem Teil des verwalteten Heaps komprimiert wird und nicht im gesamten Heap. Zweitens ist die Lebensdauer von neueren Objekten kürzer als die von älteren Objekten. Drittens tendieren neuere Objekte dazu, miteinander in Beziehung zu stehen, und die Anwendung greift etwa zur gleichen Zeit auf sie zu.

Neue Objekte werden durch den Garbage Collector der Common Language Runtime im Bereich der Generation 0 abgespeichert. Objekte, die zu einem früheren Zeitpunkt im Lebenszyklus der Anwendung erzeugt wurden und bereits Garbage Collections überstanden haben, werden höher gestuft und als Generation 1 bzw. 2 gespeichert. Der Prozess der Höherstufung von Objekten wird weiter unten in diesem Abschnitt beschrieben. Da es weniger Zeit beansprucht, statt des gesamten verwalteten Heaps nur einen Teil davon zu komprimieren, ist es bei diesem Konzept auch möglich, bei einer Garbage Collection lediglich Speicher einer bestimmten Generationsstufe freizugeben und nicht den des gesamten verwalteten Heaps.

Tatsächlich wird eine Garbage Collection erst dann eingeleitet, wenn der Bereich von Generation 0 voll ist. Versucht eine Anwendung, ein neues Objekt zu erzeugen, und der Bereich der Generation 0 ist voll, wird vom Garbage Collector ermittelt, dass es in diesem Bereich keinen freien Adressraum mehr gibt, der dem neuen Objekt zugewiesen werden kann. Daraufhin wird eine Garbage Collection gestartet, um im Bereich der Generation 0 freien Adressraum zu schaffen. Dabei werden nur die Objekte im Bereich der Generation 0 untersucht und nicht alle Objekte im verwalteten Heap. Das ist der effizienteste Weg, da neue Objekte zu einer kürzeren Lebensdauer tendieren. Es ist daher zu erwarten, dass beim Starten der Garbage Collection viele Objekte im Bereich der Generation 0 nicht mehr von der Anwendung benötigt werden. Außerdem liefert eine Garbage Collection im Bereich der Generation 0 oftmals ausreichend Speicherplatz, sodass von der Anwendung weitere neue Objekte erzeugt werden können.

Nach Durchführung einer Garbage Collection im Bereich der Generation 0 wird der Speicher für die erreichbaren Objekte komprimiert. Lesen Sie dazu die Informationen unter Freigeben von Arbeitsspeicher weiter oben in diesem Abschnitt. Anschließend werden diese Objekte vom Garbage Collector höher gestuft, und der entsprechende Bereich des verwalteten Heaps wird der Generation 1 zugeordnet. Da Objekte, die Garbage Collections überstehen, der Tendenz nach eine längere Lebenszeit haben, ist ihre Einstufung in eine ältere Generation sinnvoll. Auf diese Weise müssen die Objekte der Generationen 1 und 2 nicht bei jeder Garbage Collection, die für Objekte der Generation 0 durchgeführt wird, erneut vom Garbage Collector untersucht werden.

Nach der Durchführung der ersten Garbage Collection für Objekte der Generation 0 und der Einstufung der erreichbaren Objekte in die Generation 1 wird der verbliebene Speicherplatz für den Bereich der Generation 0 festgestellt. Neuen Objekten wird solange Speicher im Bereich der Generation 0 zugewiesen, bis dieser Bereich voll ist und erneut eine Garbage Collection gestartet werden muss. An diesem Punkt wird vom Optimierungsmodul des Garbage Collectors ermittelt, ob auch die Objekte der älteren Generationen untersucht werden müssen. Dies ist z. B. notwendig, wenn nach einer Garbage Collection im Bereich der Generation 0 nicht so viel Speicherplatz freigegeben wurde, wie von der Anwendung zum Erzeugen neuer Objekte benötigt wird. In diesem Fall kann zuerst im Bereich der Generation 1 und danach im Bereich der Generation 0 eine Garbage Collection durchgeführt werden. Kann auch dadurch nicht ausreichend Speicherplatz freigegeben werden, werden die Objekte aller drei Generationen untersucht. Nach jedem Durchlauf wird der Speicher der Generation 0 komprimiert, und die erreichbaren Objekte werden der Generation 1 zugeordnet. Entsprechend werden übrig gebliebene Objekte der Generation 1 in Generation 2 eingestuft. Da vom Garbage Collector nur drei Generationen unterstützt werden, verbleiben Objekte der Generation 2 auf dieser Stufe, bis in einem späteren Durchlauf ihre Unerreichbarkeit festgestellt wird.

Freigeben von Speicher für nicht verwaltete Ressourcen

Für die meisten von einer Anwendung erzeugten Objekte führt der Garbage Collector alle Aufgaben der Speicherverwaltung automatisch aus. Allerdings ist bei nicht verwalteten Ressourcen explizites Bereinigen erforderlich. Die häufigsten nicht verwalteten Ressourcen sind Objekte, die als Wrapper einer Betriebssystemressource dienen, z. B. ein Dateihandle, ein Fensterhandle oder eine Netzwerkverbindung. Obwohl der Garbage Collector die Lebensdauer eines verwalteten Objekts, das eine nicht verwaltete Ressource kapselt, verfolgen kann, stehen ihm keine genauen Informationen zum Bereinigen dieser Ressource zur Verfügung. Wenn Sie ein Objekt erstellen möchten, das eine nicht verwaltete Ressource kapselt, ist es ratsam, Code zum Bereinigen der nicht verwalteten Ressource in einer öffentlichen Dispose-Methode bereitzustellen. Durch Bereitstellen der Dispose-Methode wird Benutzern des Objekts die Möglichkeit gegeben, nicht mehr benötigten Speicherplatz explizit freizugeben, sobald das Objekt nicht mehr verwendet wird. Wenn Sie ein Objekt verwenden, das eine nicht verwaltete Ressource kapselt, sollten Sie die Dispose-Methode beachten und bei Bedarf aufrufen. Weitere Informationen zum Bereinigen nicht verwalteter Ressourcen und ein Beispiel für ein Entwurfsmuster zum Implementieren der Dispose-Methode finden Sie unter Garbage Collection.

Siehe auch

Aufgaben

Technologiebeispiel für Garbage Collection

Konzepte

Der verwaltete Ausführungsprozess

Referenz

GC

Weitere Ressourcen

Garbage Collection