Sei sulla pagina 1di 39

1

SUPPORTO RUN-TIME

Organizzazione della memoria


2

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

Codice Dati Statici Heap Memoria libera Stack

La dimensione del codice determinata a tempo di compilazione

Allocazione statica e allocazione dinamica


4

Statica

Dinamica

Compile time Dipende solo dal codice

Run time Dipende anche dai dati in ingresso Strategie


Memorizzazione

nello

stack Memorizzazione sullo heap

Storia dellallocazione della memoria


Allocazione statica (Static allocation) Una caratteristica del Fortran (fino al Fortran77) Implca che le dimenzioni degli arrays sia conosciuta a compile time no funzioni o sottoprogrammi ricorsivi Nessuna allocazione dinamica di strutture dati Grande Efficienza: laccesso ai dati di norma diretto Sicurezza Nessuna possibilita di out of memory a run-time

Storia dellallocazione della memoria


Stack Allocation Introdotta con Algol58 Il tempo di vita (lifetime) collegato con lattivazione delle procedure Implementato attraverso lutilizzo dei record di attivazione (stack frame):

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

Allocazione della memoria stack


7

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)

Parametri attuali (se possibile si utilizza 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

Esempio di sequenza di attivazione


11

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

Accesso alle variabili non locali in assenza di procedure annidate


13

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.

Accesso allo scope tramite display


15

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

Allocazione della memoria


16

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

Gestione della memoria


17

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

spaziale Efficienza del programma Basso overhead

18

Localit Gerarchie di memoria Frammentazione della memoria


Best-fit

e next-fit Gestione e fusione dello spazio libero

Allocazione esplicita su Heap


19

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

Allocazione esplicita su Heap


20

Dangling references
free( P->next );

10

Allocazione esplicita su Heap


21

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

Perch Garbage Collect?


22

Quando eseguiamo list = list->next; // cancella il primo elementoelement Il programma dovrebbe deallocare gli oggetti condivisi? (questo complesso ma corretto)

11

Allocazione/Deallocazione Esplicita. Problemi?


23

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

Cella = data contenuti nell heap


Le

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

Garbage: record allocati nellheap che non accessibili nel programma:


Blocchi

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

Perche fare GC?


26

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

Ogni macchina virtuale fornisce un gc:


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;

p = new node(); q = new node(); q = p; delete p;

node p, q;

14

Perch Garbace Collection?


29

Attualmente I programmi consuma memoria senza alcuna attenzione


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

La gestione di memoria esplicita non compatibile con un alto livello di astrazione

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

Il Garbage Collector Perfetto


31

Nessun impatto visibile sullesecuzione del programma Opera con qualsiasi programma e sulle sue strutture dati:

Ad esempio gestisce le strutture dati ciclliche

Recupera tutte le celle garbage (e solo quelle garbage) rapidamente

Incrementale, pu soddisfare requisiti real-time


No eccessivo paging, nessun effetto negativo sulla cache Soddisfa sempre le richieste di allocazione e non provoca frammentazione della memoria heap.

Ha un eccellente gestione della localita spaziale dei riferimenti

Gestisce lheap efficentemente

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

Tecniche moderne: GC generazionale

Reference Counting
34

Una tecnica semplice utilizzata in molti sistemi,


eg, Unix usa tenere traccia di quando un file pu essere cancellato C++ smart pointer (e.g., auto_ptr) usa il e reference counter

Ogni oggetto contiene un contatore che traccia il numero di


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.

Pseudo-codice per Reference Counting


36

// 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;

rc il campo reference count delloggetto

18

Reference Counting
37

Richiesta di memoria Extra Ogni oggetto deve contenere un campo extra per il reference counter.

Dopo lassegnazione P = null;

Reference Counting: Example


38

Heap space root set

1 2

19

Benefici del Reference Counting


39

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

Riuso immediato delle celle libere


se

RC == 0, la cella aggiunta alla put free list

Benefici del Reference Counting


40

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

Reference Counting: problemi


41

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.

Reference Counting: Cycles


42

Heap space root set

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.

Questo permette di utilizzare meno memoria per i reference counts


22

Mark-Sweep Garbage Collection


45

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

Partendo dai roots, aggiorna il mark bit di tutte le celle live


Inserisce tutte le celle non marcate nella free list Reset il mark bit di tutte le celle marcate

Fase di Sweep

Mark-Sweep (aka Mark-Scan) Algorithm


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

Mark-Sweep Example (1)


47

Heap space root set

Mark-Sweep Example (2)


48

Heap space root set

24

Mark-Sweep Example (3)


49

Heap space root set

Mark-Sweep Example (4)


50

Heap space root set

Free unmarked cells

Viene resettato Il mark bit Delle celle marcate

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.

Ottimizzazioni: Stack esplicito


52

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

Ottimizzazioni: Inversione dei puntatori (pointer reversal)


53

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

Ottimizzazione: Array di freelist


55

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.

Mark-Sweep Costs and Benefits


56

Vantaggi
Gestisce
1

le strutture cicliche Nessun space overhead


bit utilizzato per marcarre le celle

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

Lo heap viene diviso in due parti:


from-space (dove si trovano idati) to-space (parte vuota) .

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.

Copying a Linked List


58

[Cheneys algorithm]

from-space
root A

pointer forwarding address

B C D

to-space
A B C D

Cells in to-space are packed

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.

Copying Collector Tradeoffs


62

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

Confronto fra Mark-Sweep e Copying Collection


65

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

Generational Garbage Collection


68

Osservazione: la maggior parte delle celle, che muoino, muoiono giovani

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

Dividere l'heap in generazioni, e GC le cellule pi giovani pi frequentemente


34

Fractions Found to be Garbage?


69

Various experimental observations about various systems

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

Example with Immediate Aging (1)


C root set A B D Young

F G

Old

35

71

Example with Immediate Aging (2)


C root set E D Young

F B

Old

Generations with Semi-Spaces


72

root set

Youngest . . . From-space To-space

Middle generation(s)

Oldest From-space To-space

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.

Layout dei dati


78

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

Potrebbero piacerti anche