Garbage Collection unter .NET
Garbage Collection Optimierung
Finalization
Resurrection
Vorteile von Garbage Collection
Kein explizites Freigeben von Speicher
Beseitigung möglicher Fehlerquellen
Keine Fragmentierung des Heaps
Performance-Vorteil
- Garantierte „locality of reference“ bei Erzeugung
- Durch Verdichtung des Heaps rücken langlebige Objekte zusammen
Mark & Compact
Markieren aller erreichbaren Objekte,
Entfernen aller nicht markierten
Mark & Compact
Verdichtung des Heaps, Neuberechnung der
Zeiger (auch members)
Optimierung durch Generationen
Für die meisten Applikationen gilt:
- Je neuer ein Objekt, desto kürzer die Lebenszeit, und je älter, desto länger wird die Lebenszeit sein
- Neuere Objekte haben stärkere Beziehungen untereinander und werden öfter verwendet
Und: Teile des Heaps zu bearbeiten ist
effizienter als den gesamten zu verdichten
Optimierung durch differenzierte Behandlung
von Objekten unterschiedlichen Alters
Generationen
Neue Objekte sind „Generation 0“
Objekte, die einen GC-Lauf überleben, erreichen die nächsthöhere Generation
Generationen: Performance
GC kann Freigabe auf Generation 0 beschränken
- Da neuere Objekte meist kurze Lebensdauer haben, wird in Gen. 0 meist mehr Speicher freigegeben als in den anderen
Beschränkung der Mark-Traversierung
- Innere Referenzen werden nur verfolgt bei
• neuen Objekten
• alten Objekte, auf die seit der letzen Collection geschrieben wurde
Multi-Threading
Wenn GC eine Collection starten will,
müssen alle Threads angehalten werden
Mechanismen
- Fully interruptible code
- Safe Points (vom JITC in Code eingefügt)
Für unmanaged code
- „Hijacking“ um Thread anzuhalten
- Thread kann während Collection weiterlaufen
- „pinned objects“ erforderlich
Finalization
Ressourcen müssen freigegeben werden
- GC vergibt Speicher, gibt ihn wieder frei
- aber nicht möglich für unbekannte Ressourcen
• Netzwerkverbindungen
• Dateihandles
• Datenbanken, etc.
Objekt kann durch Finalization Ressourcen
freigeben, wenn es durch GC entfernt wird
Finalization – Code Beispiel
Public class NetworkObj { NetworkResource res;
Public NetworkObj() {
res = new NetworkResource();
}
...
~NetworkObj() { res.close();
} }
Finalization: Behandlung
Enthält ein Objekt eine Finalize-Methode, wird ein Zeiger auf das Objekt in die Finalization Queue gestellt (bei Erzeugung des Objekts)
Finalization: Behandlung
Wird ein Objekt als Garbage betrachtet, und befindet sich ein Zeiger darauf im Fin.-Queue, wird dieser in den „F-reachable Queue“ kopiert
Finalization: Behandlung
Objekte im F-reachable Queue können noch NICHT freigegeben werden, da erst ihre Finalize-Methode aufgerufen werden muß
Finalization: Behandlung
Separater Thread wird gestartet, der die Objekte im F-reachable Queue abarbeitet
Objekte können erst im nächsten GC-Lauf tatsächlich freigegeben werden!
Finalization Nachteile
Erzeugung von Objekten mit Finalize- Methode dauert länger
Freigabe dauert länger (Aufrufe der Methoden erforderlich)
Speicher wird nicht sofort freigegeben
Ist kein (deterministischer) Destruktor
- Darf nicht direkt aufgerufen werden
- Zeitpunkt und Reihenfolge der Ausführung sind willkürlich
- Nur verwenden wenn wirklich nötig
Dispose
Die meisten Ressourcen wollen möglichst früh wieder freigegeben werden
Unteilbare Ressourcen (z.B. Datei) können durch die Nicht-Vorhersagbarkeit des
Freigabe-Zeitpunktes problematisch sein
Lösung:
- (manuell aufrufbare) Freigabe-Methode
- Finalize als Backup
Dispose
In der Dispose-Methode kann durch
System.GC.SuppressFinalize(this);
der Zeiger aus dem Finalization Queue entfernt werden, Finalize wird nicht
aufgerufen
Vorteile:
- Wird explizit Dispose() aufgerufen (Normalfall), wird das Freigeben effizienter
- Wird vergessen, gibt der GC die Ressourcen frei
Resurrection
F-reachable Queue Zeiger werden als Wurzelzeiger aufgefaßt
- D.h. das Objekt ist tot, wird in den Queue
verschoben und lebt wieder bis zur nächsten Collection (der Zeiger ist dann entfernt)
Objekt kann in Finalize eine globale oder statische Referenz auf sich selbst erzeugen
- wird „resurrected“, da wieder erreichbar
- Allerdings wird Finalize das nächste Mal nicht mehr ausgeführt
Resurrection
Nutzen von Resurrection
- z.B. für Objekt-Pool
- hilfreich für Objekte mit zeitintensiven
Konstruktoren (z.B. Datenbankverbindung)
Im Normalfall nicht verwenden, da schwer zu
durchschauen
WeakReference
Direkte Referenzen sind „strong references“
„weak references“ für Objekte, die man
später zwar wieder braucht, die das System aber bei Bedarf freigeben darf
sr = new SomeObject();
WeakReference wr = new WeakReference(sr);
sr = null;
...
sr = (SomeObject)wr.Target;
if (sr==null) sr = new SomeObject();
Direkte Interaktion mit dem GC
System.GC.Collect();
System.GC.Collect(int generation);
System.GC.GetGeneration(object);
System.GC.GetTotalMemory();
System.GC.KeepAlive(object);
System.GC.SuppressFinalize(object);
System.GC.ReRegisterForFinalize(object);
System.GC.WaitForPendingFinalizers();
Literatur
- http://msdn.microsoft.com/library/default.asp?
url=/msdnmag/issues/1100/GCI/TOC.ASP
- http://msdn.microsoft.com/msdnmag/issues/1200/GCI2/default.
aspx
- Kevin Burton: .NET Common Language Runtime Unleashed, Sams Publishing 2002
- Dave Stutz, Ted Neward, Geoff Shilling: Shared Source CLI Essentials. O'Reilly 2003