Best practice per facilitare la raccolta dei rifiuti in .Net

In Microsoft.Net, la garbage collection è un meccanismo adottato dal Common Language Runtime (CLR) per ripulire le risorse consumate dall'applicazione. Quando crei oggetti in .Net, vengono archiviati nell'heap gestito. Sebbene sia necessario creare oggetti, nella maggior parte dei casi non devi preoccuparti di ripulire gli oggetti: il runtime lo farebbe per te.

Tuttavia, dovresti adottare le migliori pratiche nella tua applicazione per facilitare la raccolta dei rifiuti e aiutarla a ripulire le risorse più velocemente. Sebbene .Net sia abile nel reclamare oggetti gestiti, dovresti seguire alcune linee guida per facilitare una più rapida raccolta dei rifiuti e migliorare le prestazioni della tua applicazione. In questo articolo vorrei presentare una discussione su come funziona la garbage collection e le migliori pratiche coinvolte per facilitare la garbage collection in .Net.

Quando avviene la raccolta dei rifiuti?

La raccolta dei rifiuti avviene quando il sistema ha poca memoria fisica disponibile o il GC.Collect()metodo viene chiamato esplicitamente nel codice dell'applicazione. Gli oggetti che non vengono più utilizzati o non sono raggiungibili dalla radice sono candidati per la garbage collection. In sostanza, il garbage collector pulisce la memoria occupata da oggetti che non hanno riferimenti.

Generazioni

Il runtime organizza l'heap gestito in generazioni. Utilizza queste generazioni per organizzare oggetti di breve e lunga durata. Va notato che il garbage collector funziona molto più frequentemente nelle generazioni inferiori rispetto a quelle superiori. La generazione 0 contiene gli oggetti di breve durata come gli oggetti temporanei. Quando un oggetto viene creato, viene memorizzato nella generazione 0 a meno che non sia un oggetto di grandi dimensioni. Se l'oggetto è un oggetto di grandi dimensioni, viene archiviato nel Large Object Heap (LOH) nella generazione 2. Nella maggior parte dei casi, gli oggetti di generazione 0 vengono recuperati dal Garbage Collector quando viene eseguito in background.

Durante la scrittura del codice, è necessario attenersi a determinate best practice. Ad esempio, dovresti creare oggetti nell'ambito locale il più possibile per facilitare la raccolta dei rifiuti. Gli oggetti creati nell'ambito superiore generalmente risiedono nella memoria per un periodo di tempo più lungo. È possibile sfruttare il profiler CLR per comprendere i modelli di allocazione dell'applicazione.

Dovresti evitare di chiamare il GC.Collect()metodo poiché causa una raccolta completa di tutte le generazioni (generazione 0, 1 e 2). Quando si effettua una chiamata al GC.Collect()metodo, il runtime visita tutti gli oggetti attivi nell'applicazione. Questa operazione richiede una notevole quantità di tempo e, quindi, è un'operazione molto costosa. Di conseguenza, non è una buona pratica chiamare il GC.Collect()metodo.

Se devi chiamare il GC.Collect()metodo, dovresti chiamare GC.WaitForPendingFinalizers()dopo la chiamata a GC.Collect()per assicurarti che il thread in esecuzione corrente aspetti che i finalizzatori per tutti gli oggetti siano stati eseguiti.

Successivamente, è necessario effettuare GC.Collect()nuovamente una chiamata al metodo per assicurarsi di raccogliere gli oggetti morti che rimangono. Questi oggetti morti che potrebbero essere stati creati a causa della chiamata al metodo finalizer sugli oggetti. Il frammento di codice seguente mostra come vengono utilizzati questi metodi.

System.GC.Collect();

System.GC.WaitForPendingFinalizers();

System.GC.Collect();

È necessario assicurarsi di ridurre al minimo le allocazioni nascoste e scrivere il codice in modo tale da eliminare le possibilità di promozione di oggetti di breve durata alle generazioni superiori. Non dovresti fare riferimento a oggetti di breve durata da quelli di lunga durata per evitare la promozione degli oggetti di breve durata alle generazioni superiori.

Dovresti anche evitare di scrivere finalizzatori per le tue classi. Se hai un finalizzatore implementato nella tua classe, gli oggetti di tali classi diventerebbero oggetti di lunga durata poiché il runtime ha bisogno di promuovere gli oggetti finalizzabili alle generazioni precedenti. È necessario impostare gli oggetti su null prima di effettuare una chiamata a esecuzione prolungata se tali oggetti non sono necessari per l'applicazione. Se non hai più bisogno di un oggetto statico o di altri oggetti nella tua applicazione, dovresti impostarlo su null prima di effettuare una chiamata a lunga esecuzione. Non impostare le variabili locali su null poiché non è necessario; il runtime può determinare quale oggetto locale non è referenziato nel codice o non è più utilizzato, quindi non è necessario impostare esplicitamente alcuna variabile locale su null.