Come un Garbage Collector segue i puntatori per scoprire oggetti dal vivo

3

Ho letto Un tour di V8: Garbage Collection e sono bloccato su questa parte:

Distinguishing pointers and data on the heap is the first problem any garbage collector needs to solve. The GC needs to follow pointers in order to discover live objects.

Comprendo il resto del documento per la maggior parte, parlando di come eseguire effettivamente la garbage collection una volta che hai gli oggetti live. Ma non capisco come tu identifichi gli oggetti live.

Dopo quella citazione, descrivono 3 modi per distinguere i puntatori dai dati, ma il secondo è un po 'nascosto:

As long as we can identify what class an object comes from, we can find all of its pointers.

Sembra carino, mi piacerebbe sapere come farlo. Presumo che intendano quando costruisci una nuova classe come new Foo() , sotto la cappa associa l'istanza della classe con un puntatore alla classe reale. Ma non vedo come il garbage collector utilizzi queste informazioni per determinare quali oggetti sono vivi o meno.

Allo stesso modo, questa risorsa è un po 'vaga su lo stesso dettaglio:

The garbage collector works in two phases: the mark phase, and the sweep phase. In the mark phase, the garbage collector starts walks object references in order to find objects that are still reachable. The garbage collector starts at a few basic places where the object references are stored and given names (the stack, and global storage, and static storage), and then traverses references in the objects.

Questo è un po 'più utile, ma comunque carino vaghi:

Tracing garbage collection traverses the object graph forward, starting with the roots, to find the live data. Reference counting traverses the object graph forward, starting with the anti-roots (the set of objects whose reference counts were decremented to 0), to find dead data.

Intuitively, one can think of tracing as operating on “matter” and reference counting as operating on “anti-matter”.

Non vedo come attraversare gli oggetti e sapere che è pronto per essere raccolto o no. Qualche consiglio su come capire quella parte sarebbe di aiuto. Grazie!

    
posta Lance Pollard 21.08.2018 - 01:39
fonte

2 risposte

7

Sono sicuro che la risposta di Erik ha molte ottime informazioni ma penso che tu sia bloccato sui fondamentali. Sono più familiare con Java GC ma i principi di base sono piuttosto universali.

I understand the rest of the document for the most part, talking about how to actually perform the garbage collection once you have the live objects. But I don't understand how you identify the live objects.

La cosa che penso tu manchi è che esiste necessariamente una serie di riferimenti di root. In Java è il tuo stack, i valori statici, ecc. In JS, suppongo che sia qualcosa come l'oggetto window . Penso che un'immagine aiuti qui:

Icerchirappresentanooggettielefreccesonoriferimentiadaltrioggetti.Asinistrahoetichettatotreoggetticomepartedeiriferimentidiroot.Ciòcheèesattamentenonèterribilmenteimportante,maquellochedevisapereèchequestisonofondamentalmenteimpliciti/codificati.Sonofondamentaliperl'ambienteruntime,quindinonènecessariotrovarli.Sappiamocosaedovesonointrinsecamente.

Pensochel'algoritmopiùsemplicedacapiresiailmark-sweepechesiagarantitotrovaretuttiirifiutiadifferenzadialtriapproccicomeilconteggiodeiriferimenti.Ilprimopassoècontrassegnare.

Hoaggiuntounsegnodispuntaverdeaciascunoggettoacuisifariferimentodalleradici.Dovrestiessereingradodiverificaresehoottenutocorrettamente(perfavore!)Iniziaredaognirooteseguireisuoiriferimenti.Quindiseguiiriferimentidaquellifinoaquandononcisarannopiùpercorsidaseguire.Questoèil"live set" di oggetti.

Ora arriva lo sweep:

Tuttociòchenonèstatoidentificatocomedalvivoèconsideratopartedel"set morto". Questi sono ciò che può essere rimosso. Si noti il ciclo di riferimenti nell'angolo in basso a destra. Questi sono tutti nel set morto anche se ci sono riferimenti a loro. Questo perché non esiste alcun percorso dalle radici a nessuno di questi oggetti.

    
risposta data 21.08.2018 - 18:40
fonte
5

Distinguishing pointers and data on the heap is the first problem any garbage collector needs to solve. The GC needs to follow pointers in order to discover live objects.

In generale, ci sono due approcci per identificare riferimenti e non riferimenti:

  1. Metadati che ti dice se una variabile è una variabile di riferimento o altrimenti. Questi metadati ti diranno anche dove si trova la variabile, sia in un registro per una certa durata, sia in una posizione di memoria. Tali metadati vengono generati durante la generazione del codice. In generale, il codice generato verrà reso consapevole della necessità di questi metadati, e questo può avere alcuni effetti minori sul registro e sulle scelte di posizione della memoria, in modo che i metadati non siano eccessivamente complessi. Tuttavia, tutte le informazioni che l'identificatore delle radici degli oggetti ha bisogno di sapere è se ogni posizione di memoria è un puntatore o meno, ovunque sia sospeso un metodo (ad es. Nei siti di chiamata, sospensione nello stack).

  2. Indovinare, che si ottiene osservando i valori per vedere se potrebbero trovarsi nell'intervallo dell'heap garbage collocato. Un tale approccio, per essere corretto, deve essere prudente, quindi assumere determinati valori come indicatori, anche se potrebbero non esserlo. Un tale approccio potrebbe essere trovato negli approcci alla garbage collection per C / C ++ (sì, esistono!) Dove il compilatore non è noto per aiutare, e dobbiamo preoccuparci dei numeri interi che contengono puntatori. Questo probabilmente non verrebbe utilizzato per altri linguaggi che sono più progettati per supportare l'approccio dei metadati. (Si noti inoltre che l'ipotesi impedisce la compattazione poiché non possiamo rischiare di modificare interi che assomigliano a dei puntatori.)

As long as we can identify what class an object comes from, we can find all of its pointers.

Nei runtime raccolti da garbage, spesso scopriremo che il modello di oggetto prescrive che il primo slot dell'oggetto sia un puntatore vtable o un altro riferimento di classe. Tale riferimento è fondamentalmente nascosto all'utente, anche se utilizzato per eseguire cast, chiamate di metodi virtuali, trovare interfacce e individuare riferimenti all'interno dell'oggetto.

The garbage collector works in two phases: the mark phase, and the sweep phase. In the mark phase, the garbage collector starts walks object references in order to find objects that are still reachable. The garbage collector starts at a few basic places where the object references are stored and given names (the stack, and global storage, and static storage), and then traverses references in the objects.

Alcuni riferimenti sono visti come radici dal vivo, quindi tutto ciò che queste radici raggiungono (o potrebbero raggiungere) è vivo e dovrebbe essere considerato dal vivo e quindi non essere raccolto. Le radici costituiscono tutti i thread (vale a dire i loro stack, le attivazioni aka, ovvero i frame stack) e anche tutte le variabili statiche / globali. I metadati che stiamo analizzando in precedenza descrivono i frame dello stack e le variabili statiche / globali.

I don't see how you can traverse objects, and know that it is ready to be garbage collected or not. Any tips on understanding that part would be of help

Non lo facciamo: qualsiasi oggetto attraversato è necessariamente considerato dal vivo. Il trucco è quindi identificare gli oggetti che non sono attraversati dall'ultimo traversal che inizia da queste radici. Pertanto, gli oggetti sono etichettati come vivi o sconosciuti / morti. (Durante l'attraversamento abbiamo live vs. non visitato, e post traversal abbiamo live vs dead.) A volte il bit per questo tag viene rubato dal puntatore vtable o class nel primo slot, poiché ci sono bit (generalmente parlando) che dovrebbe sempre essere zero al LSB di tali puntatori (i puntatori devono quindi essere usati con maggiore attenzione). Durante la traccia, gli oggetti che sono noti per essere raggiungibili sono contrassegnati come tali, mentre gli oggetti che non sono rimasti non modificati.

Potresti pensare che tutti gli oggetti dovrebbero prima essere contrassegnati come irraggiungibili, al fine di identificare quelli che non sono raggiunti, e tuttavia la maggior parte degli algoritmi GC non richiama questa inizializzazione, anche se questo è dovuto al fatto che c'è un trucco che può essere usato per invertire il senso di questo bit live / sconosciuto su ogni attraversamento della raccolta GC: quindi i nodi visitati sono contrassegnati con "1" su una raccolta GC (e quelli contrassegnati con "0" vengono raccolti come garbage) mentre su alla prossima raccolta, usiamo "0" per segnare quelli raggiungibili, e quelli non raggiungibili tengono ancora "1" dalla raccolta precedente.

Da questo si può vedere che è necessario essere in grado di trovare gli oggetti non controllati rimanenti, operazione che può essere eseguita attraversando l'heap in ordine di memoria, dato che in genere i modelli oggetto prescrivono che ogni oggetto ha un riferimento vtable o un puntatore di classe nel primo slot, così puoi facilmente dire quanto è grande l'oggetto e quindi dove si trova l'oggetto successivo sequenzialmente nell'heap gc.

Anche nel caso in cui i metadati identificano le radici, usate come inizio di oggetti vivi (attraversamento), queste radici potrebbero essere conservative nel senso che alcune radici potrebbero non essere effettivamente utilizzate in seguito. Quindi, mentre in un approccio orientato ai metadati sappiamo con certezza se una variabile è un riferimento o meno, non sappiamo in realtà se la variabile verrà o meno utilizzata successivamente nell'algoritmo del codice (cioè i thread). In questo senso c'è un certo conservatorismo in questo approccio. Alcuni approcci di compilatore / ottimizzazione (ad esempio incorporati nei metadati) possono ridurre la finestra della vivacità potenziale, in modo tale che anche se alcune variabili possono ancora contenere un riferimento è noto che non devono essere utilizzate, ad esempio, dopo l'ultimo utilizzo della variabile anche se il suo spazio di archiviazione contiene ancora un riferimento.

    
risposta data 21.08.2018 - 01:52
fonte

Leggi altre domande sui tag