Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 1 / 20 Thomas Negeli | 0255604 [email protected] Reference Counting I Negeli Thomas (0255604 / 521) Student an der Johannes Kepler Universität Linz, Österreich Präsentation für: Seminar Garbage Collection WS2005 339.372 [email protected] Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 2 / 20 Thomas Negeli | 0255604 [email protected] Invariante: RC = 0 Zelle ist unbenutzt Zeiger auf Zelle vom Heap oder vom Stack setzen inkrement des RC Zeiger löschen dekrement des RC Algorithmus 1 - Anforderung von Speicher: /** Speicherzelle anlegen */ MemoryCell New() { if (free_list == null) /** Abbruch, kein Speicher * mehr frei */ System.exit(1); newCell = allocate(); newCell.rc = 1; //ReferenceCount return newCell; } /** Speicher anfordern */ MemoryCell allocate() { newCell = free_list.pop(); free_list = free_list.next; return newCell; } free_list: Menge an freien Speicherzellen newCell: einfache freie Speicherzelle allocate(): liefert eine neue freie Speicherzelle new(): verwaltet Referenzzähler und Speicherplatz Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 3 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 2 – Überschreiben von Zellen: /** Update von Objekten */ void Update(MemoryCell r, MemoryCell s) { delete(r); s.rc++; r = s; } Update(r,s) überschreibt r mit s /** Löschen von Referenzen */ void delete(MemoryCell t) { t.rc--; if(t.rc == 0) { if (t.next != null) delete(t); free(t); } } delete(t) löscht rekursiv alle Kinder von t /** Speicher freigeben */ void free(MemoryCell t) { t.next = free_list; free_list = t; } free(t) gibt den Speicher von t frei Reference Counting I Seminar Garbage Collection 339.372 | WS2005 Thomas Negeli | 0255604 [email protected] Folie 4 / 20 Stärken und Schwächen des Reference Counting Vorteile Einfache Handhabung der Algorithmen Speicherverwaltung und Programmausführung laufen verschränkt ab bietet sich für Echtzeitsysteme an Problem: Rekursivität von Algorithmus 2 durch Löschen der Kinder Räumliche Lokalität der Referenzen Zelle ohne Zugriff auf andere Zellen im Heap zurückfordern (Kinder bilden Ausnahme) Problem: ev. Cache Miss bzw. Page Fault Studien belegen: wenige Zellen sind shared, viele existieren nur kurzzeitig Algorithmus 1 und 2 erlauben sofortige Wiederverwendung Weniger Page Faults, Cache Misses als Tracing Strategien leicht abgeänderte Kopie : Zeiger ausborgen (Glasgow Haskell compiler) finalisation actions Nachteile Aufwand zur Aufrechterhaltung der Invariante Werte in alter und neuer Zelle justieren Starke Kopplung an das Speichermanagement des Benutzerprogrammes bzw. Compilers Zerbrechliche Strategie Extra Speicherplatz in jeder Zelle Maximaler RC Wert = Anzahl der Pointer Problem mit zyklischen Datenstrukturen Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 5 / 20 Thomas Negeli | 0255604 [email protected] Löschen des Zeigers von R nach S macht den Teilbaum S, T, U unzugänglich Freigabe aufgrund der RC Werte nicht möglich Lösung: Reference Counting bis Heap voll ist Anschließend Tracing: * Alle Counts auf 0 setzen * inkrementieren jeder aktiven Zelle in der Markierungsphase * unmarkierte Zellen löschen Kleinere Count Felder möglich, Tracing verwaltet maximale Zählerstände Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 6 / 20 Thomas Negeli | 0255604 [email protected] Problem: rekursives Löschen der Kinder eines Knotens in der delete(…) Methode Strategien überlegen um Rekursivität zu entfernen, da Löschaufwand vom Subgraph abhängt Algorithmus 3 + 4 – Weizenbaum‘s lazy freeing: /** Speicherzelle anlegen */ MemoryCell New() { if (free_list == null) /** Abbruch, kein Speicher * mehr frei */ System.exit(1); newCell = allocate(); if(newCell.next != null) delete(newCell); newCell.rc = 1; //ReferenceCount return newCell; } delete(t) = if (t.rc==1) { t.rc = free_list free_list = t } else decrementRC(t) Löschen der Kinder erfolgt nicht mehr bei der Freigabe der Zelle, sondern bei der Anforderung von neuem Speicher. RC Feld dient nun der Verkettung der freien Zellen t ist ein Zeiger auf ein Heap Element. Zeiger speichert den Count Wert, nicht die Zelle. Problem nur teilweise gelöst: Schneller bei kaskadierten Freigaben. Problem wurde nur verschoben, eventuell Verschwendung von Speicherplatz. Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 7 / 20 Thomas Negeli | 0255604 [email protected] Verwaltungsaufwand macht RC unattraktiv gegenüber Tracing Strategien (scheinbar) • Überschreiben von Zeigern erfordert einigen Aufwand • Sogar Unterprogrammaufrufe erfordern Justierungen • Caches werden oft mit nicht benötigten Daten gefüllt Daten werden verändert und müssen wieder zurückgeschrieben werden obwohl wieder der Ausgangszustand hergestellt wurde Lösung: Unnötige Veränderung der Count Werte nach Möglichkeit vermeiden (aufschieben) Manuell, bei Wissen über Unterprogramme, kein Problem, jedoch aufwändig Optimierung durch Compiler (Anpassungen nötig) Besser: Verwaltung zur Laufzeit durch z.B. Deutsch-Bobrow Algorithmus Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 8 / 20 Thomas Negeli | 0255604 [email protected] Studien haben gezeigt: Hauptteil von Zeigermanipulationen erfolgt im Stack. Andere Operationen haben einen Anteil von 1% Operationen auf lokale Variablen optimieren Algorithmus 5 – nach Deutsch-Bobrow Kein RC für lokale Variablen einfache Zuweisungen bei Manipulationen möglich, Update weniger komplex Count Werte bedeuten jetzt Referenzen von anderen Heap Objekten Keine Freigabe wenn RC = 0, einfügen in eine Zero Count Table (ZCT) /** Update von Objekten */ void Update(MemoryCell r, MemoryCell s) { delete(r); s.rc++; ZCT.remove(s); r = s; } /** Löschen von Referenzen */ void delete(MemoryCell t) { t.decrementRC(); if (t.rc==0) { ZCT.put(t); } } Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 9 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 6 – Aufräumen des Speichers Aufräumen des Speichers in 3 Stufen: 1. 2. 3. void reconcile() { /** markieren der Elemente */ for(i=0; i<stack.getSize(); i++) { stack.getElem(i).incrementRC(); } Alle Elemente der ZCT mit Referenzen aus dem Stack werden markiert Alle nicht markierten Elemente entfernen Alle Markierungen entfernen /** unreferenzierte Zellen * löschen */ for(i=0; i<ZCT.getSize(); i++) { cell = ZCT.getElement(i); if (cell.rc == 0) { if (cell.next!=null) delete(cell); free(cell); } } /** Markierungen entfernen */ for(i=0; i<stack.getSize(); i++) { stack.getElem(i).decrementRC(); } } Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 10 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 7 – größter gemeinsamer Teiler /** Größter gemeinsamer Teiler * Bedingung: x >= y >= 0 */ int gcd(int x, int y) { if (y == 0) return x; t = x-y; if (x>t) return gcd(y,t); else return gcd(t,y); } Getroffene Konventionen: • Alle Objekte werden am Heap abgelegt • Ausdrücke werden als Graph dargestellt dessen Knoten Objekte am Heap sind • Der System Stack enthält Zeiger auf Heap Objekte • Atomare Objekte werden mit ihrem Wert bezeichnet Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 11 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 7 – größter gemeinsamer Teiler /** Größter gemeinsamer Teiler * Bedingung: x >= y >= 0 */ int gcd(int x, int y) { if (y == 0) return x; t = x-y; if (x>t) return gcd(y,t); else return gcd(t,y); } Getroffene Konventionen: • Alle Objekte werden am Heap abgelegt • Ausdrücke werden als Graph dargestellt dessen Knoten Objekte am Heap sind • Der System Stack enthält Zeiger auf Heap Objekte • Atomare Objekte werden mit ihrem Wert bezeichnet Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 12 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 7 – größter gemeinsamer Teiler /** Größter gemeinsamer Teiler Getroffene Konventionen: * Bedingung: x >= y >= 0 */ • Alle Objekte werden am Heap abgelegt int gcd(int x, int y) { • Ausdrücke werden als Graph dargestellt dessen Knoten Objekte am Heap sind if (y == 0) return x; • Der System Stack enthält Zeiger auf Heap Objekte t = x-y; • Atomare Objekte werden mit ihrem Wert bezeichnet if (x>t) return gcd(y,t); Update(right(R),6) else return gcd(t,y); Update(left(R),B) } Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 13 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 7 – größter gemeinsamer Teiler /** Größter gemeinsamer Teiler * Bedingung: x >= y >= 0 */ int gcd(int x, int y) { if (y == 0) return x; t = x-y; if (x>t) return gcd(y,t); else return gcd(t,y); } Zustand nach reconcile() Getroffene Konventionen: • Alle Objekte werden am Heap abgelegt • Ausdrücke werden als Graph dargestellt dessen Knoten Objekte am Heap sind • Der System Stack enthält Zeiger auf Heap Objekte • Atomare Objekte werden mit ihrem Wert bezeichnet Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 14 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 7 – größter gemeinsamer Teiler /** Größter gemeinsamer Teiler * Bedingung: x >= y >= 0 */ int gcd(int x, int y) { if (y == 0) return x; t = x-y; if (x>t) return gcd(y,t); else return gcd(t,y); } Das Spiel beginnt wieder von Vorne. Getroffene Konventionen: • Alle Objekte werden am Heap abgelegt • Ausdrücke werden als Graph dargestellt dessen Knoten Objekte am Heap sind • Der System Stack enthält Zeiger auf Heap Objekte • Atomare Objekte werden mit ihrem Wert bezeichnet Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Thomas Negeli | 0255604 [email protected] Folie 15 / 20 reconcile() wird bei Überlauf der ZCT gestartet Problem wenn mehrere Zellen gleichzeitig freigegeben werden Mögliche Lösungen: 1. Freigabe der Zelle die den Überlauf verursacht wird erst beim nächsten reconcile() durchgeführt 2. Anpassung des Weizenbaum Algorithmus. Zeiger erst bei erneuter Anforderung der Zelle löschen, tritt dann ein Überlauf auf, reconcile() anstoßen. Verfahren reduziert Kosten bei Schreibvorgängen auf Zeiger %-Werte der absoluten Ausführungszeit. Versuch mit Smalltalk Programmiersprache Nachteil des Verfahrens: keine sofortige Freigabe von Speicher der nicht mehr benötigt wird (erst im reconcile()) Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 16 / 20 Thomas Negeli | 0255604 [email protected] Problem des Speicherplatzes beim Reference Count Feld Schlimmster Fall: max. mögliche Anzahl Zeiger einer Architektur speichern ABER: Praktisch nie so hohe Zählerstände Speicher sparen, jedoch Überläufe behandeln Algorithmus 8 – sticky Zählerstände /** Referenzzähler Dekrement mit * Sticky Wert */ public void decrementRC() { if (this.rc < sticky) this.rc--; } /** Referenzzähler Inkrement mit * Sticky Wert */ public void incrementRC() { if (this.rc < sticky) this.rc++; } 1. Maximal zulässiger Wert eines Zählers darf nicht überschritten werden 2. Maximaler Wert erreicht bleibt sticky Reference Counting kann Zellen die sticky sind nie mehr freigeben! Tracing nötig Reference Counting I Seminar Garbage Collection 339.372 | WS2005 Folie 17 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 9 – einfacher Mark & Sweep Strategie: 1. Heap durchwandern und alle Count Werte auf 0 setzen 2. Markieren aller aktiven Zellen void mark_sweep() { /** löschen aller Count Werte */ for(i=0; i<Heap.SIZE; i++) { Heap.getCell(i).rc = 0; } /** markieren aller referenzierten Zellen */ for(i=0; i<Stack.SIZE; i++) { mark(Stack.getCell(i)); } 3. Zelle erreicht ursprünglichen Wert an Referenzen bzw. sticky Wert /** bereinigen des Speichers */ sweep(); if (free_list==null) /** Abbruch, kein Speicher mehr frei */ System.exit(1); } mark(…) arbeitet rekursiv. void mark(MemoryCell mc) { mc.incrementRC(); if(mc.rc==1) { /** rekursiv alle Kinder markieren */ if(mc.next!=null) mark(mc.next); } } Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 18 / 20 Thomas Negeli | 0255604 [email protected] Reduzierung des Count Feldes auf 1 Bit Zelle entweder sticky oder nicht (nicht genauer) Hauptteil der Zellen ist nicht shared Optimierungen hier ansetzen Ziele: 1. Hinauszögern der Garbage Collection 2. Verwaltungsspeicherplatz reduzieren 3. Vermeidung unnötiger Kopien z.B. Arrays mit tausenden Elementen Algorithmus 10 – 1 Bit Counting Update(R,S) = delete(*R) T = sticky(*S) if RC(*S) == unique *S = T *R = T Sticky Bit wandert von der Zelle in den Zeiger = Tagging Auslesen der Zelle selbst vermeiden weniger Cache Misses bzw. Page Faults Reference Counting + Tracing um Zellen wieder unique machen zu können Count Bit vom Zeiger in den Knoten mark Bit von mark_sweep() benutzen Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 19 / 20 Thomas Negeli | 0255604 [email protected] Algorithmus 11 – Ought to be Two Cache N = select(N) Software Cache für Zellen deren Wert eigentlich 2 ist /** Update von Objekten */ void Update(MemoryCell r, MemoryCell s){ if(s.rc==unique) insert(s); delete(r); r = s; } /** Cache füllen */ void insert(MemoryCell s) { if(hit(s)) /** markieren wenn bereits * im Cache */ s.rc = sticky; else cache.put(s); } /** Löschen von Referenzen */ void delete(MemoryCell t) { if(!hit(t)) { if(t.rc == unique) { if(t.next!=null) delete(t.next); free(t); } } /** Überprüfen ob Zelle bereits im * Cache ist */ boolean hit(MemoryCell s) { if(cache.contains(s)) { cache.remove(s); return true; } else return false; } Seminar Garbage Collection 339.372 | WS2005 Reference Counting I Folie 20 / 20 Thomas Negeli | 0255604 [email protected] Reference Counting per Hardware self-managing heap memories based on reference counting Aktiver Speicher mit Intelligenz = RCM (Reference Counting Memory) Bänke Einfacher Einsatz in Multiprozessor Systemen Spezielle Architektur hohe Entwicklungskosten hohe Kosten für den Endkunden da wenig Absatz Lösung: vorgaukeln einer regulären Speicherbank Standard Datenspeicher + RCM Bereich Jeder Bereich hat eigenen Bus und eigene Ports Je Bereich eine eigene Liste an freiem speicher Performance abhängig von Problemgröße