Come fa un garbage collector a impedire che l'intera memoria venga scannerizzata su ogni raccolta?

16

Alcuni garbage collector (almeno di Mono e di .NET) hanno un'area di memoria a breve termine che scansionano spesso e un'area di memoria secondaria che scansionano meno spesso. Mono chiama questo vivaio.

Per scoprire quali oggetti possono essere eliminati, eseguono la scansione di tutti gli oggetti a partire da root, stack e registri e dispongono di tutti gli oggetti a cui non si fa più riferimento.

La mia domanda è come impediscono la scansione di tutte le memorie in uso su ogni raccolta? In linea di principio, l'unico modo per scoprire quali oggetti non sono più in uso è scansionare tutti gli oggetti e tutti i loro riferimenti Tuttavia, ciò impedirebbe al sistema operativo di scambiare la memoria anche se non è in uso dall'applicazione e si sente come una grande quantità di lavoro che deve essere fatto, anche per "Raccolta Nursery". Non sembra che stiano vincendo molto usando un asilo nido.

Mi manca qualcosa o il garbage collector esegue effettivamente la scansione di ogni oggetto e ogni riferimento ogni volta che esegue una raccolta?

    
posta Pieter van Ginkel 24.08.2012 - 17:03
fonte

6 risposte

14

Le osservazioni fondamentali che consentono alla garbage collection generazionale di evitare di dover eseguire la scansione di tutti gli oggetti di generazione precedente sono:

  1. Dopo una raccolta, tutti gli oggetti ancora esistenti avranno una generazione minima (ad esempio in .net, dopo una raccolta Gen0, tutti gli oggetti sono Gen1 o Gen2; dopo una raccolta Gen1 o Gen2, tutti gli oggetti sono Gen2).
  2. Un oggetto, o parte di esso, che non è stato scritto poiché una raccolta che ha promosso tutto alla generazione N o successiva non può contenere riferimenti ad oggetti di generazioni inferiori.
  3. Se un oggetto ha raggiunto una determinata generazione, non è necessario identificarlo come raggiungibile per garantirne la conservazione quando raccoglie le generazioni inferiori.

In molti framework GC, è possibile che il garbage collector contrassegni gli oggetti o parti di essi in modo tale che il primo tentativo di scriverli attiverà un codice speciale per registrare il fatto che sono stati modificati. Un oggetto o una parte di esso che è stato modificato, indipendentemente dalla sua generazione, deve essere scansionato nella prossima raccolta, poiché potrebbe contenere riferimenti a oggetti più recenti. D'altra parte, è molto comune che ci siano molti vecchi oggetti che non vengono modificati tra le raccolte. Il fatto che scansioni di bassa generazione possano ignorare tali oggetti può consentire a tali scansioni di completarsi molto più rapidamente di quanto altrimenti farebbero.

Si noti, btw, che anche se non si riesce a rilevare quando gli oggetti vengono modificati e si deve eseguire la scansione di tutti i passaggi GC, la garbage collection generazionale potrebbe comunque migliorare le prestazioni dello "sweep" di un raccoglitore compattato. In alcuni ambienti embedded (specialmente quelli dove c'è poca o nessuna differenza di velocità tra gli accessi di memoria sequenziali e casuali), spostare i blocchi di memoria attorno è relativamente costoso rispetto ai riferimenti di tagging. Di conseguenza, anche se la fase del "segno" non può essere accelerata usando un collettore generazionale, può essere utile accelerare la fase di "sweep".

    
risposta data 24.08.2012 - 18:12
fonte
7

I GC a cui ti stai riferendo sono garbage collector generazionali . Sono progettati per ottenere il massimo da un'osservazione nota come "mortalità infantile" o "ipotesi generazionale", il che significa che la maggior parte degli oggetti diventa irraggiungibile molto rapidamente. Effettuano infatti una scansione partendo dalle radici, ma ignorano tutti i vecchi oggetti . Pertanto, non è necessario scansionare la maggior parte degli oggetti in memoria, eseguono solo la scansione di oggetti giovani (a scapito del mancato rilevamento di oggetti vecchi non raggiungibili, almeno non a quel punto).

"Ma è sbagliato", ti sento urlare, "i vecchi oggetti possono e fanno riferimento a oggetti giovani". Hai ragione, e ci sono diverse soluzioni a ciò, che ruotano tutte intorno a ottenere conoscenze, in modo rapido ed efficiente, su quali vecchi oggetti devono essere controllati e quali sono sicuri da ignorare. Si riducono praticamente a registrare oggetti, o piccoli (più grandi di oggetti, ma molto più piccoli dell'intero heap) gamme di memoria che contengono indicazioni per le generazioni più giovani. Altri hanno descritto quelli molto migliori di me, quindi ti darò solo un paio di parole chiave: Marcatura della carta, serie di ricordi, barriere di scrittura. Esistono anche altre tecniche (compresi gli ibridi), ma queste comprendono gli approcci comuni di cui sono a conoscenza.

    
risposta data 24.08.2012 - 18:11
fonte
3

Per scoprire quali oggetti vivai sono ancora in vita, il collector deve solo scansionare il set di root e qualsiasi vecchio oggetto che è stato mutato dall'ultima raccolta , poiché un vecchio oggetto che non ha stato recentemente mutato non può indicare un oggetto giovane. Esistono diversi algoritmi per mantenere queste informazioni a diversi livelli di precisione (da un insieme esatto di campi mutati a un insieme di pagine in cui può essersi verificata una mutazione), ma generalmente implicano una sorta di barriera di scrittura : codice che viene eseguito su ogni mutazione del campo tipizzata di riferimento che aggiorna la contabilità del GC.

    
risposta data 27.08.2012 - 03:54
fonte
1

La più vecchia e più semplice generazione di garbage collector ha effettivamente eseguito la scansione di tutta la memoria e ha dovuto interrompere tutte le altre elaborazioni mentre lo facevano. Gli algoritmi successivi sono migliorati in questo modo in vari modi: rendendo la copia / scansione incrementale o eseguita in parallelo. La maggior parte dei moderni raccoglitori di rifiuti segrega gli oggetti in generazioni e gestisce con cura i puntatori intergenerazionali per raccogliere le nuove generazioni senza disturbare quelle più vecchie.

Il punto chiave è che i garbage collector lavorano in stretta collaborazione con il compilatore e con il resto del runtime per mantenere l'illusione che stia guardando tutta la memoria.

    
risposta data 25.08.2012 - 03:38
fonte
-2

In sostanza ... GC utilizza "bucket" per separare ciò che è in uso e ciò che non lo è. Una volta verificato, cancella le cose che non sono in uso e sposta tutto il resto alla 2a generazione (che viene controllata meno spesso della 1a generazione) e sposta le cose che sono ancora in uso in 2nd den a 3rd gen.

Quindi, le cose in 3a generazione sono di solito oggetti che rimangono bloccati per qualche motivo, e GC non controlla molto spesso.

    
risposta data 24.08.2012 - 17:07
fonte
-5

L'algoritmo solitamente usato da questo GC è il marchio Naïve - e -sweep

dovresti anche essere consapevole del fatto che questo non è gestito dal C # stesso, ma dal cosiddetto CLR .

    
risposta data 24.08.2012 - 17:14
fonte

Leggi altre domande sui tag