Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
SUPPORTO RUN-TIME
Per chi progetta un compilatore il programma target in esecuzione su uno spazio di indirizzamento logico. Assumeremo
che la memoria a run time sia costituita da una sequenza di byte tra loro contigui Che lo spazio di memoria indirizzabile pi piccolo sia il byte e che 4 byte formino una word Oggetti di dimenzione maggiore di un byte saranno memorizzati in spazi contigui
La rappresentazione a run time di un programma oggetto nello spazio di indirizzamento logico consiste di aree dati e aree programmi.
La quantit di memoria richiesta per memorizzare un dato dipende dal suo tipo La struttura di memoria dei vari tipi legata alla macchina target
Padding
Statica
Dinamica
nello
Differenti invocazioni istanziano differenti versioni delle variabili locali; il valore delle variabili locali non persiste fra successive invocazioni del sottoprogramma La dimenzione delle strutture dati locali pu dipendere da un parametro supportata le ricorsionerecursion La dimenzione del valore ritornato da una funzione deve essere nota a compile time Gli oggetti possono essere allocati dinamicamente nello stack ma sono comunque deallocati quando termina la procedure
Alla chiamata di un sottoprogramma una certa quantit di memoria (per variabili locali etc) viene impilata nello stack Quando il sottoprogramma termina tale memoria viene rimossa dallo stack Cio permette di compilare i sottoprogrammi in modo che lindirizzo relativo delle sue variabili locali sia sempre lo stesso indipendentemente dalla chiamata
Albero di attivazione
8
Dato che le chiamate di procedura sono annidate nel tempo possiamo utilizzare una rappresentazione di tale chiamate utilizzando un albero detto albero di attivazione Le attivazioni sono riportate sullalbero seguendo da sx a dx lordine in cui sono state chiamate Un figlio deve terminare prima che possa iniziare lattiviazione alla sua destra
Record di attivazione
9
Le chiamate e i ritorni sono gestiti con uno stack di controllo. Ogni attivazione aperta ha un record di attivazione nello stack di controllo Il generico record di attivazione (dipende dal linguaggio) contiene Valori temporanei Dati locali alla procedura Lo stato della macchina salvato costituito genericamente dallindirizzo di ritorno e tutti i valori dei registri al momento della chiamata Un access link (riferimento per localizzare i dati appartenenti allo scope ma non definiti allinterno del sottoprogramma Un control link; riferimento al record di attivazione del programma chiamante Valore di ritorno (si preferisce utilizzare un registro)
Sequenze di chiamate
10
Sequenza di chiamata: codice che si occupa di allocare il record di attivazione nello stack e riempirne i campi. Tale codice dipende dal linguaggio sorgente, dal linguaggio target e dal sistema operativo. Tale codice suddiviso tra chiamante e chiamato Di norma
Le informazioni passate dal chiamante al chiamato sono in genere posizionati allinizio del record di attivazione I dati di dimensione fissa sono posizionati nella parte centrale del record di attivazione Gli elementi di dimenzione non nota a priori vengono posti alla fine del record Lo stack pointer (top.sp) punta alla parte di record di attivazione di dimenzione fissa
Il chiamato
Valuta i parametri attuali Memorizza lindirizzo di ritorno e il valore del puntatore top.sp nel record di attivazione del chiamato Salva i valori dei registri e le altre informazioni di stato Inizia i suoi dati locali e inizio lesecuzione
Parametri attuali e valore di ritorrno Control link Stato salvato Dati temporanei e locali Parametri attuali e valore di ritorrno Control link Stato salvato Dati temporanei e locali chiamato
chiamante
Sequenza di ritorno
12
Il chiamato
Posiziona il valore di ritorno subito dopo i parametri Ripristina il registro top.sp e gli altri registri quindi passa il
controllo allindirizzo di controllo salvato fra informazioni di stato. Recupera il valore dello stack pointer
Lo scope in tal caso costituito dalle variabili locali e dalle variabili globlali
Le variabili globali vengono allocate nella parte di memoria riservata ai dati statici Le variabili locali nel record di attivazione del sottoprogramma dove sono definite
Accesso alle variabili non locali in presenza di procedure annidate: uso dellaccess link
14
La procedura q con livello di annidamento nq richiama la procedura p con livello di annidamento np np > nq, in tal caso p deve essere definita allinterno di q in tal caso semplice ad access link di p viene copiato il valore del record di attivazione di q La chiamata ricorsiva (p=q), in questo caso laccess link della nuova attivazione identico alla precedente np < nq la procedura q deve essere annidata in r e la definizione di p deve essere immediatamente annidata in r. Il recordi di attivazione pi recente di r puo essere localizzato seguento la catena si access link per np nq + 1 passi.
un array di puntatori di dimensione pari al livello di annidamento Ciascun puntatore punta al record di attivazione pi in alto nello stack con quel livello di annidamento
Heap Allocation Lisp (~1960) Gli oggetti sono allocati e deallocati dinamicamente, in qualsiasi ordine, e il lifetimes degli oggetti non sono correlati con la invocazione delle funzioni Permette strutture dati ricorsive (eg alberi) La dimenzione delle strutture dati pu variare dinamicamente Oggetti dimenzionati dinamicamente possono esser ritornati come risultato di una funzione Permette ai linguaggi di programmazione di supportare first-class functions (che sono implementate per mezzo di closures) Molti moderni linguaggo forniscono sia stack che heap allocation
Allocazione: il gestore della memoria fornisce un blocco di memoria contigua della dimensione richiesta Deallocazione: il gestore di memoria riinserisce lo spazio di memoria nel pool della memoria libera Obiettivi del gestore della memoria
Efficienza
18
Esempi sono C, C++, Pascal dove il programma alloca e dealloca memoria heap per gli oggetti Garbage = dispersione della mamoria
Se non ci sono altri puntatori che puntano al primo alemento della lista quando termina la funzione gli oggetti diventano irragiungibili quindi garbage
Dangling references
free( P->next );
10
Dangling references which has caused P->next to become a dangling pointer (and a potential delayed effect run-time error) as well as creating some garbage
Quando eseguiamo list = list->next; // cancella il primo elementoelement Il programma dovrebbe deallocare gli oggetti condivisi? (questo complesso ma corretto)
11
Quale funzione responsabile della deallocazione di un oggetto quando non pi necessario? Gli oggetti condivisi fra diverse strutture dati rendono complesso conoscere quando viene rimossa lultimo riferimento Una soluzione comune aggiungere un reference counts agli oggetti (ma questa soluzione ha alcuni problemi) Un alternativa e duplicare (Cloning) gli oggetti in modo da eliminare il problema della condivisione degli oggetti. Questa soluzione non efficiente. Memory allocation errors are a major source of problems in C/C++ code (random crashes, memory leaks) Esistono numerosi tool per la localizzazione di errori di memory allocation (Purify, ObjectCenter, Insure++)
Celle e Liveness
24
celle sono puntate da puntaori contenuti in registri, stack, memoria globale/statica, o in altre celle dellheap
Roots: registri, allocazioni nello stack o variabili globali/statiche Una cella viva (live) se il suo indirizzo contenuto nella root o in unaltra cella viva
12
Garbage
25
allocati che non siano raggiungibili a partire dallo stack per mezzo di almeno una catena di puntatori (cell is no longer live) Ogni altro genere di memory error: esiste un riferimento ad un blocco di memoria che non e piu allocato
Garbage collection (gc) da una parte una caratteristica dei linguaggi di programmazione:
I linguaggi hanno sia new e delete, o solo new? JVM, .NET Perl, Python, SML, Haskell, Prolog ... importante per ragioni di integrita del software
Riferimenti: Richard Jones and Rafael Lins, Garbage Collection Wiley, 1996.
13
Garbage collection
27
Garbage collection (GC) - Il processo di recuperare tali record e renderli disponibili per l'allocazione di nuovi record, non viene eseguito dal compilatore, ma a runtime, dai programmi di supporto collegati al codice compilato (in quanto, ovviamente, i record presenti in memoria dipendono dai dati che sono stati inseriti dall'utente). Per individuare il garbage, l'approccio ideale sarebbe usare la definizione dinamica della liveness. Tuttavia, non essendo questa computabile, ci dobbiamo basare sull'approssimazione conservativa della raggiungibilit del record. Il compilatore dovr garantire che ogni variabile viva raggiungibile e, al tempo stesso, minimizzare il numero di variabili raggiungibili che non sono vive.
Esempio di Garbage
28
class node { int value;
node next;
node p, q;
14
1GB laptops, 1-4GB deskops, 8-512GB servers Indirizzi a 64-bit (SPARC, Itanium, Opteron) Memory leaks, dangling references, double free, misaligned addresses, null pointer dereference, heap fragmentation
e non gestiscono
Scarso uso delle referenze locali che provocano un alto cache miss rates e un eccessivo demand paging
GC e linguaggi di programmazione
30
GC non in realt una caratteristica del linguaggio GC una pratica che riguarda la gestione automatica ed efficente della memoria heap
Linguaggi cooperativi: Lisp, Scheme, Prolog, Smalltalk Linguaggi non cooperativi : C and C++ Nei quali kibrerie di garbage collection possono essere costruite Linguaggi Object-oriented: Modula-3, Java In Java, viene eseguito su un thread a bassa priorita; System.gc pu essere invocato da un programma Linguaggi funzionali: ML e Haskell
Esempi di GC
15
Nessun impatto visibile sullesecuzione del programma Opera con qualsiasi programma e sulle sue strutture dati:
Costo del GC
32
I primi sistemi Lisp furono noti per i lunghi stop nel mezzo di sessioni interattive a cauusa dellesecuzione del gc. Algoritmi migliori e processori pi veloci hanno reso il gc un problema molto minore. GC ancora da evitare per molte applicazioni realtime (dove il tempo di risposta richiesto e inferiore ai millisecondi), Per la maggior parte delle applicazioni. Il problema : Which is more important:software which is free of memory leaks and memory allocation errors, ... or software which is a little faster but less reliable and takes twice as long1 to debug?
16
Tecniche di GC
33
Reference counting
Tiene traccia direttamente delle celle vive GC viene eseguto quando un blocco heap viene allocato Non permette il recupero di tutta le celle garbage
Tracing
GC viene eseguito e identifica le celle vive quando una richiesta di memoria fallisce Mark-sweep Copy collection
Reference Counting
34
eg, Unix usa tenere traccia di quando un file pu essere cancellato C++ smart pointer (e.g., auto_ptr) usa il e reference counter
Riferimenti alloggetto; Se il contatore diventa zero, la memoria delloggetto recuperata immediatamente (ponendo in una lista libera?)
Il costo del gc distribuito sullintero programma esecuzione Richiede spazio e tempo per memoriazzare e incrementare (decrementare) il conatore ogni qualvolta un riferimento ad una cella e aggiunto (eliminato)
17
Reference counting
35
Assieme ad ogni record p, viene memorizzato un numero (reference count) che indica quanti puntatori x.fi a quel record sono presenti. Il compilatore emette istruzioni per far s che ogni volta che un nuovo x.fi punta a p, il reference count di p viene incrementato, mentre viene decrementato quello di r a cui puntava in precedenza. Se decrementando il reference count di r questo assume il valore zero, r viene inserito nella freelist. Se esistono dei record a cui r puntava, il loro refcount deve essere a sua volta diminuito di uno. Ci viene fatto non subito, ma al momento (successivo) in cui r verr rimosso dalla freelist, per i seguenti motivi: 1. viene diviso in pi parti il decremento ricorsivo, rendendo pi uida l'esecuzione del programma 2. si risparmia spazio: il codice per il decremento ricorsivo unico (nell'allocatore). Il codice per vericare se refcount ha raggiunto zero, tuttavia, deve comunque essere ripetuto per ogni decremento.
// chiamatta dal programma per creare // una nuova istanza di un oggetto function New(): if freeList == null then report an error; newcell = allocate(); newcell.rc = 1; return newcell; // chiamata dal programma per // overwrite una variabile // puntatore R with con un // altro valore S procedure Update(var R, S): if S != null then S.rc += 1; delete(*R); *R = S;
// chiamata da New function allocate(): newcell = freeList; freeList = freeList.next; return newcell; // chiamata da Update procedure delete(T): T.rc -= 1; if T.rc == 0 then foreach pointer U held inside object T do delete(*U); free(T); // chiamata da delete procedure free(N): N.next = freeList; freeList = N;
18
Reference Counting
37
Richiesta di memoria Extra Ogni oggetto deve contenere un campo extra per il reference counter.
1 2
19
overhead incrementale
GC overhead distribuito lungo tutta la computazione ==> tempo di risposta omogeneo nelle situazioni interattive. (Contrasto con stop and collect approach.) Applicazioni real-time
Semplicit dellimplementazione Pu coesistere con la gestione manuale della memoria heap Buona gestione della localit spaziale il programma accede alle locazioni di memoria che probabilmente sono sul punto di di essere utilizzare. (In contrasto con una marking phase durante la quale viene scorsa tutta la memoria) la maggioranza degli oggetti hanno una vita breve; il reference counting recuperer e riutilizzera tali oggetti rapidamente. (In contrasto con con uno schema dove gli oggetti morti rimangono inutilizzati per un lungo periodo vino a quando il prossimo gc a rilevato un out of memory.)
20
Space overhead
1 word per il contatore Ogni volta che un puntatore aggiornato: aggiornare due reference counters (decrementare il vecchio target, incrementare il sorgente). Laccesso in memoria costoso.
Time overhead
Il codice fragile (se prodotto by hand) molto facile dimenticare di incrementare o decrementare un reference counter (eg quando si passa un oggetto ad un sottoprogramma) Dimenticare un incremento ==> strange bugs, Dimenticare un decremento ==> memory leaks.
Memory leak
1 1
21
Problemi
43
Non si pu rimuovere garbage con riferimenti ciclici: se due record non sono raggiungibili, ma puntano l'uno all'altro vicendevolmente, il loro reference count rimarr a uno e non potranno essere rimossi. Soluzioni
Richiedere esplicitamente che il programmatore elimini tutti i cicli quando ha finito di usare una struttura Combinare Reference Count con qualche Mark&Sweep, per rimuovere i cicli
La gestione dei reference count computazionalmente pesante! Ogni operazione di aggiornamento di un puntatore implica l'incremento di un reference count, il decremento di quello della variabile puntata precedentemente e le operazioni per la verica del raggiungimento di refcount=0. possibile attuare qualche ottimizzazione tramite analisi dataflow, ma il peso comunque elevato.
Reference Counting
44
Strutture dati cicliche La soluzione ovvia e combinare reference counting con gli altri schemi di gc Eseguire gli altri schemi di gc quando
La memoria e esaurita Ad intervalli regolari Per esempio si possono utilizzare campi di 8 bit per il counter. Quando il counter supera il valore massimo, 255, rimane bloccato Quando viene eseguito il gc, recupera gli oggetti cil cui contatore dovrebbe essere zero e resetta i contatori degli altri oggetti.
22
Ogni cella ha un mark bit Garbage rimane non raggiungibile e inutilizzata fino a quando lheap non piena: quando agisce il GC il programma in esecuzione viene sospeso Fase di Marking
Fase di Sweep
Il primo utilizzo sembra essere in Lisp La memorizzazione per i nuovi oggetti ottenuto da un free-pool Nessuna azione extra eseguita quando il programma copia o sovrascrive i puntatori Quando il pool di libero esaurito, il New () invoca il mark-sweep gc per reinserire la memoria occupata da oggetti inaccessibili al free-pool e quindi ricomincia
23
24
25
Costo
51
Il tempo di esecuzione dell'algoritmo DFS proporzionale al numero R di nodi segnati (cio dei record raggiungibili) ed proporzionale alla dimensione dello heap H. L'esecuzione di una garbage collection quindi c1R + c2H. Il costo per unit di spazio libero sullo heap, o costo ammortizzato di raccolta, dato dal costo di esecuzione diviso per il numero H R di parole di memoria libere, cio (c1R+c2H)/(HR) . Se la memoria quasi piena (R~ H) il costo molto elevato. Se la memoria quasi vuota (H >> R), il costo molto ridotto. Se al termine del processo di collection R/H maggiore di 0.5 (o un'altro valore, secondo qualche criterio) il collector dovrebbe chiedere al sistema operativo di fornire maggiore spazio per lo heap del programma.
Se si usa un algoritmo DFS standard (ricorsivo), si potrebbe arrivare ad avere H ricorsioni, e quindi H frame sulla pila dei record di attivazione, ossia il garbage collector occuperebbe pi memoria dello HEAP stesso. Ci decisamente da evitare, perci si preferisce utilizzare un algoritmo iterativo in cui si gestisce esplicitamente una struttura a pila su cui salvare i record marcati. In tal modo, si otterr ugualmente una pila di H elementi, ma saranno singole word invece che record di attivazione.
26
Per ridurre ulteriormente lo spazio occupato, si pu osservare che, dopo che il contenuto di un campo x.fi di un record x stato salvato sullo stack, possibile riutilizzare lo spazio x.fi. In particolare, possibile utilizzarlo per salvare il puntatore al record a partire dal quale x era stato raggiunto. Secondo questo ragionamento, quindi possibile utilizzare lo stesso grafo dei puntatori per salvare anche lo stack, facendo s che, mentre si stanno esaminando i campi di un record (e i campi dei record a cui questi, a loro volta, puntano), ogni record che si sta esaminando punti al proprio predecessore (cio al record da cui puntato) invece che al proprio successore (il record a cui punta normalmente). Ogni volta che tutti i campi di un record sono stati esaminati, i suoi puntatori vengono ripristinati, ricostruendo il grafo originale.
54
function DFS(x) if (x un puntatore e il record a cui punta non marcato) imposta root come predecessore (t) marca x while true if (c' ancora un campo x.fi (contenente il valore y) da esaminare nel record x) if (y un puntatore e il record puntato da y non marcato) x.fi t //fai puntare il campo al suo predecessore (pointer reversal) t x //x il predecessore del record che stiamo esaminando ora x y //il record corrente quello puntato da y marca il record corrente x else y x //salva in y l'indirizzo del record che stavamo esaminando x t //torna ad esaminare il predecessore if (x=root) tutto il grafo stato visitato. Fine. t x.fi //il nuovo predecessore quello il cui valore era stato salvato dall'inversione x.fi y //ripristino del puntatore originale (fine del pointer reversal)
27
1.
Per ridurre la frammentazione della memoria libera si pu utilizzare, invece di un'unica freelist, un array di freelist, ognuna delle quali corrisponde ad una certa dimensione dei record ivi contenuti. In tal modo, sar possibile utilizzare il blocco di memoria pi piccolo di dimensioni sucienti a contenere la variabile da allocare. Se non ci sono blocchi sucientemente piccoli, possibile usare il pi piccolo disponibile e sprecare una parte dello spazio (se si vuole mantenere la dimensione dei blocchi), oppure suddividerlo e reimmettere nell'opportuna freelist la parte di spazio non utilizzata. La creazione delle diverse freelist demandata alla fase sweep dell'algoritmo. L'uso di array di freelist rende anche pi veloce anche l'allocazione di nuove variabili nello heap, perch l'allocatore non dovr cercare sequenzialmente in tutta la freelist un blocco di dimensioni sucienti, ma potr direttamente andarlo a richiedere alle freelist di dimensioni opportune. Frammentazione esterna: sono presenti tanti piccoli record liberi di dimensione insuciente per l'allocazione che si vuole eseguire. Frammentazione interna: non possibile trovare spazio libero a sucienza perch questo presente, inutilizzato, all'interno di record troppo grandi gi allocati.
Vantaggi
Gestisce
1
Svantaggi
Lesecuzione
del programma viene sospesa Pu influenza la gestione della memoria virtuale Pu causare un paging eccessivo se la dimenzione del working-set size piccola e lheap non tutta nella memoria fisica Lheap pu essere frammentata
28
Copying Collector
57
Attraversando il grafo dei record raggiungibili, si copiano man mano tali record nel to-space, occupando senza frammentazione i primi blocchi di memoria attigui. La copia nel to-space si dice quindi compatta.
I nodi radice (le variabili dello stack) vengono quindi fatti puntare ai dati del to-space, rendendo il from-space non pi raggiungibile.
Alla successiva esecuzione dell'algoritmo, il from-space e il to-space saranno invertiti.
[Cheneys algorithm]
from-space
root A
B C D
to-space
A B C D
29
Flipping Spaces
59 to-space
pointer forwarding address
from-space
root A B C D
Copying Collector
60
Nel from space sono presenti due puntatori: next indica il punto in cui inizia la parte di memoria libera (e quindi dove, su richiesta, verranno allocate le prossime aree di memoria), limit indica il termine dello spazio disponibile. Quando next raggiunge limit, viene eseguita la collection. Durante il normale funzionamento del sistema, next pu solo crescere: viene ridotto unicamente dall'esecuzione della collection. Quando si comincia l'esecuzione di una collection, next punta alla base del tospace, e viene incrementato di size(p) ogni volta che un record p viene copiato.
30
Copying Collector
61
Forwarding il nome dell'operazione base dell'algoritmo. Si esegue il forwarding di un puntatore p quando, dato un p che punta al from-space, lo si modica per puntare agli stessi dati nel to-space. Funziona in tre sottocasi diversi:
1.
Se p punta ad un record del from-space che gi stato copiato, allora p.f1 il forwarding pointer che indica dove si trova la copia nel tospace. riconoscibile come tale dall'indirizzo, che interno al to-space.
2.
Se p punta al from-space, il contenuto dell'indirizzo puntato viene copiato nel to-space e p.f1 viene fatto puntare alla nuova locazione del record.
Se p non un puntatore oppure se punta al di fuori dal from-space, il forwarding non fa nulla.
3.
Vantaggi: basso overhead nellallocazone Il check dellOut-of-space richiede un confronto fra indirizzi Consente un efficente allocazione di celle di dimenzioni variabili Vantaggi: compattazione Elimina la frammentazione, buona localita dei riferimenti Scantaggi: dimenzione della memoria heap disponibile
31
Algoritmo di Cheney
63
Esegue la copia tramite una visita breadth-rst. Non necessit di uno stack esterno o dell'inversione dei puntatori perch introduce, a anco a next, un puntatore scan che indica quali record sono stati scanditi: i record compresi tra scan e next, sono stati copiati nel to-space ma non ancora modicati tramite forwarding (contengono riferimenti al fromspace). I record precedenti a scan, invece, sono gi stati modicati dal forwarding (contengono solo riferimenti al to-space). Al termine del processo di collection, scan raggiunge next.
Algoritmo di Cheney
64
Localit dei riferimenti L'uso di un algoritmo breadth rst crea problemi con la localit dei riferimenti, in quanto vengono memorizzati in aree adiacenti di memoria i record che hanno la stessa distanza dalle radici. Avrebbe invece pi senso memorizzare vicini i record che sono connessi da una catena di puntatori, in quanto molto pi probabile che si dovr accedere ad essi in sequenza e, per il funzionamento delle politiche di paginazione della memoria, facilmente in questo modo tali record verranno caricati assieme riducendo il numero di page fault e cache miss, migliorando sensibilmente le prestazioni. Una ricerca depth-rst permetterebbe di ottenere maggiore localit, ma richiederebbe l'uso dell'inversione dei puntatori, risultando quindi piuttosto lenta. Un buon approccio quello ibrido, che utilizza breadth-rst ma che, dopo aver copiato un oggetto, verica tramite depth-rst se esiste qualche suo nodo glio da copiargli vicino.
32
Detti M = dimenzione dellheap R = percentuale di oggetti vivi Tutti gli oggetti vivi devono essere copiati da un Copying Collector, cosi il tempo e proporzionale a R: tcc = aR Il collettore Mark-Sweep visita tutti gli oggetti vivi e sweeps tutti gli elementi dellheap, cosi il suo tmpo ha due componenti : tms = bR +cM where a, b, c are constants
Confronto
66
Spazio recuperato dal gc mcc = M/2 R mms = M R Efficienza e = byte recuperati al secondo ecc = 1/2ar 1/a ems = (1-r)/(br + c) Dove r = R/M
33
Comparison, contd
67
Scope annidati sono entrato ed uscito pi di frequente, gli oggetti temporanei in uno scope ambito nidificato nascono e muoiono ravvicinati nel tempo Espressioni interne in Scheme sono pi giovani di espressioni esterne, in tal modo diventano garbace prima Non ci sono tutte le celle durante il ciclo di GC Periodicamente raccoglie li "vecchie generazioni Ammortizzare il costo di generazione in generazione
34
O-O languages: 80% 98% of new objects die before another 1 MB of heap space has been allocated1
Lisp: 50% 90% die before another 10KB allocated Haskell: 75% 95% die before another 10KB allocated Cedar: 99% die before another 721KB allocated SML/NJ: 98% of all objects collected as garbage at each gc
The above observations are justification for treating young objects in a special way
70
F G
Old
35
71
F B
Old
root set
Middle generation(s)
36
73
Riduce sia il tempo sia lo spazio usati dal metodo copying collection. Si basa sull'osservazione che molti oggetti sono destinati a morire presto, ma se un oggetto sopravvissuto gi ad alcune collection, probabile che sopravviva a lungo. L heap diviso in generazioni: G0 contiene gli oggetti pi giovani. Tutti gli oggetti di G1 sono pi vecchi di quelli di G0; gli oggetti in G2 sono pi vecchi di quelli di G1 e cos via. Ogni area dovrebbe essere esponenzialmente pi grande della precedente (es: G0 = 0.5MB,G1 = 2MB,G2 = 8MB). Un oggetto che sopravvissuto a due o tre collection viene promosso a quella successiva.
74
Al momento di eseguire una collection (si pu usare sia il metodo mark&sweep sia copying collection) la si esegue prima su G0 e poi, se necessario, sulle generazioni via via successive. Per trovare gli elementi di G0, non basta guardare le variabili nello stack: potrebbero esistere anche riferimenti all'interno di oggetti vecchi che puntano a oggetti pi nuovi. Questa condizione (per fortuna rara, perch richiede l'aggiornamento di un campo di un oggetto vecchio) renderebbe l'analisi del solo G0 pi lenta che non l'attraversamento di tutto il grafo a partire dalle radici. Per ovviare al problema facciamo si che il programma compilato ricordi dove ci sono puntatori da oggetti vecchi ad oggetti nuovi, in modo da poterli trovare immediatamente. Esistono diversi metodi per fare ci.
37
75
1.
2.
3.
4.
Remembered list: viene generata una lista di oggetti di cui un campo stato aggiornato. Al momento del GC si cercano all'interno della lista gli oggetti che puntino dentro G0 Remembered set La remembered list potrebbe contenere duplicati di uno stesso oggetto al suo interno. Per evitare il problema, si codica all'interno dello stesso oggetto un bit per indicare l'appartenenza o meno al remembered set. Card marking La memoria divisa in schede di dimensione 2k. Un oggetto pu occupare una o pi schede, o parte di una scheda. Ogni volta che un indirizzo b viene modicato, la scheda contenente quell'indirizzo viene marcata (tramite un bit in un apposito array) Page marking Simile al precedente, ma invece di introdurre una gestione manuale delle schede, si usa la paginazione della memoria. Quando un indirizzo viene modicato, un dirty bit viene impostato per quella pagina. In ogni caso, all'inizio della garbage collection, il set di oggetti ricordati indica quali oggetti potrebbero contenere puntatori a G0. Questi oggetti assieme alle variabili dello stack fungono da root variables per l'algoritmo di ricerca scelto.
Incremental collection
76
Per ridurre il tempo di interruzione dell'esecuzione di un programma durante il GC, possibile usare algoritmi incrementali, cio che non conducono l'intero processo di collection in un unico passaggio, ma lo eseguono un pezzo alla volta inframezzandosi alla normale esecuzione del programma.
Collector:l'algoritmo di collection Mutator: il programma in esecuzione (in quanto in grado di modicare i dati su cui si sta lavorando).
Generalmente gli algoritmi incrementali si basano su un processo di segnatura a tre colori per stabilire l'avanzamento dell'algoritmo. I record possono essere:
Bianchi oggetti non ancora visitati dall'algoritmo di ricerca depth-rst o breadth-rst. Grigi gli oggetti sono gi stati visitati, ma i loro gli no ( come se si trovassero sulla pila, o tra scan e next) Neri sia gli oggetti sia tutti i loro gli sono gi stati visitati e marcati ( come se fossero gi stati tolti dalla pila, o se fossero prima di scan).
38
Incremental collection
77
A partire dalle radici, si visitano tutti gli oggetti, rendendoli prima grigi, visitando i loro figli e infine rendendoli neri. Quando non sono pi presenti oggetti grigi, tutto ci che rimasto bianco garbage. Due invarianti sono comuni a tutti gli algoritmi che si basano su questo principio: Gli oggetti neri non possono puntare a oggetti bianchi Ogni oggetto grigio sulla struttura dati (pila o coda) del collector. Il mutator, durante il suo lavoro, deve fare attenzione a non rompere tali invarianti, introducendo opportune politiche di coloratura: ad esempio, nell'algoritmo di Dijkstra, Lamport e altri, quanto il mutator salva un puntatore bianco a dentro ad unoggetto nero b, deve colorare a di grigio.
Il collector deve essere in grado di operare su qualunque tipo di dato, anche complesso. Deve quindi essere in grado di determinare 1. la dimensione occupata dai dati di ogni record, il numero di campi da cui formato e se questi campi contengono o meno un puntatore 2. La prima word di un oggetto generalmente viene fatta puntare ad un record di descrizione del tipo (o della classe) che contiene l'indicazione della dimensione e la posizione dei campi puntatore. Tale record deve essere stato creato dal compilatore grazie alle informazioni ricavate dall'analisi semantica. Il compilatore deve anche permettere al collector di identicare se qualunque temporary (registro o area di memoria) contiene un puntatore. Ci viene fatto compilando un'apposita mappa dei puntatori (pointer map) ogni volta che potrebbe essere necessario eseguire una collection, cio ad ogni operazione alloc.
39