Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Le app WinUI create con Windows App SDK e scritte in C# ottengono la gestione automatica della memoria dal Garbage Collector .NET. Questo articolo riepiloga le procedure consigliate per il comportamento e le prestazioni per .NET Garbage Collector nelle app WinUI. Per altre informazioni sul funzionamento di .NET Garbage Collector e sugli strumenti per il debug e l'analisi delle prestazioni di Garbage Collector, vedere Garbage Collection.
Annotazioni
La necessità di intervenire nel comportamento predefinito del Garbage Collector è fortemente indicativa di problemi di memoria generali con l'app. Usare lo strumento di utilizzo della memoria nello strumento Visual Studio e le linee guida in Garbage collection e prestazioni per identificare gli oggetti che sopravvivono alle raccolte.
Il Garbage Collector determina quando eseguire bilanciando il consumo di memoria dell'heap gestito con la quantità di lavoro che deve essere eseguita da un'operazione di Garbage Collection. Uno dei modi in cui il Garbage Collector esegue questa operazione consiste nel dividere l'heap in generazioni e raccogliere solo parte dell'heap nella maggior parte del tempo. Nell'heap gestito sono presenti tre generazioni:
- Generazione 0. Questa generazione contiene oggetti appena allocati, a meno che non siano di 85 KB o superiori, nel qual caso fanno parte dell'heap di oggetti di grandi dimensioni. L'heap di oggetti di grandi dimensioni viene raccolto con raccolte di seconda generazione. Le raccolte di generazione 0 sono il tipo più frequente di raccolta e la pulizia di oggetti di breve durata, ad esempio variabili locali.
- Generazione 1. Questa generazione contiene oggetti che sono sopravvissuti a raccolte di generazione 0. Funge da buffer tra la generazione 0 e la generazione 2. Le raccolte di generazione 1 si verificano meno frequentemente rispetto alle raccolte di generazione 0 e puliscono gli oggetti temporanei attivi durante le raccolte di generazione 0 precedenti. Una raccolta di generazione 1 raccoglie anche la generazione 0.
- Generazione 2. Questa generazione contiene oggetti di lunga durata che sono sopravvissuti a raccolte di generazione 0 e generazione 1. Le raccolte di seconda generazione sono le meno frequenti e raccolgono l'intero heap gestito, incluso l'heap di oggetti di grandi dimensioni, che contiene oggetti di 85 KB o superiori.
È possibile misurare le prestazioni del sistema di raccolta automatica dei rifiuti in due modi: la durata dell'operazione di raccolta dei rifiuti e il consumo di memoria dell'heap gestito. Se si ha una piccola app con dimensioni heap inferiori a 100 MB, concentrarsi sulla riduzione del consumo di memoria. Se si dispone di un'app con un heap gestito superiore a 100 MB, concentrarsi solo sulla riduzione del tempo di Garbage Collection. Ecco come aiutare .NET Garbage Collector a ottenere prestazioni migliori.
Ridurre il consumo di memoria
Riferimenti al rilascio
Un riferimento a un oggetto nell'app impedisce che l'oggetto e tutti gli oggetti a cui fa riferimento vengano raccolti. Il compilatore .NET esegue un buon lavoro per rilevare quando una variabile non è più in uso, in modo che gli oggetti mantenuti da tale variabile siano idonei per la raccolta. In alcuni casi, tuttavia, potrebbe non essere ovvio che alcuni oggetti hanno un riferimento ad altri oggetti perché parte dell'oggetto grafico potrebbe essere di proprietà delle librerie usate dall'app. Per informazioni sugli strumenti e sulle tecniche per scoprire quali oggetti sopravvivono al processo di Garbage Collection, consulta Garbage Collection e performance.
Indurre un'operazione di Garbage Collection (raccolta della memoria inutilizzata) se è utile
Indurre un'operazione di Garbage Collection solo dopo aver misurato le prestazioni dell'app e aver determinato che l'induzione di una raccolta migliorerà le prestazioni.
È possibile indurre un'operazione di Garbage Collection di una generazione chiamando GC. Collect(n), dove n è la generazione da raccogliere (0, 1 o 2).
Annotazioni
È consigliabile non forzare un'operazione di Garbage Collection nell'app perché il Garbage Collector usa molte euristiche per determinare il tempo migliore per eseguire una raccolta e forzare una raccolta è in molti casi un uso non necessario della CPU. Tuttavia, se si sa di avere un numero elevato di oggetti nell'app che non vengono più usati e si vuole restituire questa memoria al sistema, potrebbe essere opportuno forzare un'operazione di Garbage Collection. Ad esempio, puoi indurre una raccolta alla fine di una sequenza di caricamento in un gioco per liberare memoria prima dell'avvio del gioco. Per evitare inavvertitamente di indurre troppe operazioni di Garbage Collection, è possibile impostare GCCollectionMode su Optimized. Questo istruisce il Garbage Collector ad avviare una raccolta solo se determina che la raccolta sarebbe sufficientemente produttiva da essere giustificata.
Ridurre il tempo di raccolta dei rifiuti
Questa sezione si applica se hai analizzato l'app e hai osservato tempi di garbage collection lunghi. I tempi di sospensione correlati a Garbage Collection includono il tempo necessario per eseguire un singolo passaggio di Garbage Collection e il tempo totale impiegato dall'app per le operazioni di Garbage Collection. Il tempo necessario per eseguire una raccolta dipende dalla quantità di dati in tempo reale che l'agente di raccolta deve analizzare. Le dimensioni della generazione 0 e della generazione 1 sono limitate, ma la generazione 2 continua a crescere poiché nella tua app rimangono attivi più oggetti di lunga durata. Ciò significa che i tempi di raccolta per la generazione 0 e la generazione 1 sono associati, mentre le raccolte di seconda generazione possono richiedere più tempo. La frequenza con cui le operazioni di Garbage Collection vengono eseguite dipende principalmente dalla quantità di memoria allocata, perché un'operazione di Garbage Collection libera memoria per soddisfare le richieste di allocazione.
Il Garbage Collector sospende occasionalmente l'app per eseguire il lavoro, ma non necessariamente per l'intera durata della raccolta. I tempi di pausa in genere non sono percepibili dall'utente nell'app, soprattutto per le raccolte di generazione 0 e generazione 1. La funzionalità Background Garbage Collection di .NET Garbage Collector consente l'esecuzione in modo concorrente delle raccolte di generazione 2 mentre l'app è in esecuzione e sospende l'app solo per brevi intervalli di tempo. Ma non è sempre possibile eseguire una raccolta di seconda generazione come raccolta in background. In tal caso, la pausa può essere percepibile dall'utente se si dispone di un heap sufficientemente grande (più di 100 MB).
Le operazioni frequenti di Garbage Collection possono contribuire ad un maggiore utilizzo della CPU, a un aumentato consumo energetico, a tempi di caricamento più lunghi o a una diminuzione del frame rate nell'applicazione. Di seguito sono riportate alcune tecniche che è possibile usare per ridurre i tempi di Garbage Collection e le pause correlate alla raccolta nell'app WinUI gestita.
Ridurre le allocazioni di memoria
Se non si allocano oggetti, il Garbage Collector non viene eseguito a meno che non ci sia una condizione di scarsa memoria nel sistema. La riduzione della quantità di memoria allocata si traduce direttamente in raccolte per l'eliminazione della memoria meno frequenti.
Se in alcune sezioni della tua app le pause sono completamente indesiderate, puoi pre-allocare in anticipo gli oggetti necessari durante un tempo meno critico per le prestazioni. Ad esempio, un gioco potrebbe allocare tutti gli oggetti necessari per il gioco durante la schermata di caricamento di un livello e non effettuare allocazioni durante il gioco. In questo modo si evitano pause mentre l'utente sta giocando il gioco e può comportare una frequenza di fotogrammi più elevata e coerente.
Ridurre le raccolte di generazione 2 evitando oggetti con una durata media
Le Operazioni di Garbage Collection generazionali sono ottimali quando nell'app sono presenti oggetti di breve durata e/o di lunga durata. Gli oggetti di breve durata vengono raccolti nelle raccolte di generazione 0 e generazione 1 più economiche e gli oggetti di lunga durata vengono promossi alla generazione 2, che vengono raccolti raramente. Gli oggetti di lunga durata sono quelli in uso per l'intera durata dell'app o durante un periodo significativo della tua app, ad esempio durante una pagina o un livello di gioco specifico.
Se si creano spesso oggetti con durata temporanea ma con durata sufficiente per essere promossi alla generazione 2, si verificano più raccolte di generazione 2 costose. È possibile ridurre le raccolte di generazione 2 riciclando gli oggetti esistenti o rilasciando oggetti più rapidamente.
Un esempio comune di oggetti con una durata a medio termine è costituito da oggetti utilizzati per la visualizzazione di elementi in un elenco che un utente scorre. Se gli oggetti vengono creati quando gli elementi vengono visualizzati quando gli elementi vengono scorsi nell'elenco e non vengono più referenziati quando gli elementi vengono eliminati dall'elenco, l'app in genere include un gran numero di raccolte di generazione 2. In situazioni come questa, è possibile pre-allocare e riutilizzare un set di oggetti per i dati visualizzati attivamente all'utente e usare oggetti di breve durata per caricare informazioni man mano che gli elementi dell'elenco vengono visualizzati.
Ridurre le raccolte di generazione 2 evitando oggetti di grandi dimensioni con durate brevi
Qualsiasi oggetto di dimensioni pari a 85 KB o superiore viene allocato nell'heap di oggetti di grandi dimensioni (LOH) e viene raccolto come parte della generazione 2. Se sono presenti variabili temporanee, ad esempio buffer, maggiori di 85 KB, una raccolta di seconda generazione li pulisce. La limitazione delle variabili temporanee a meno di 85 KB riduce il numero di raccolte di seconda generazione nell'app. Una tecnica comune consiste nel creare un pool di buffer e riutilizzare oggetti dal pool per evitare allocazioni temporanee di grandi dimensioni.
Evitare oggetti ricchi di riferimenti
Il Garbage Collector determina quali oggetti sono attivi seguendo i riferimenti tra gli oggetti, a partire dalle radici dell'applicazione. Per ulteriori informazioni, vedere Cosa accade durante un'operazione di raccolta rifiuti. Se un oggetto contiene molti riferimenti, c'è più lavoro da fare per il garbage collector. Una tecnica comune, soprattutto con oggetti di grandi dimensioni, consiste nel convertire oggetti avanzati di riferimento in oggetti senza riferimenti. Ad esempio, anziché archiviare un riferimento, archiviare un indice. Naturalmente questa tecnica funziona solo quando è logicamente possibile farlo.
La sostituzione dei riferimenti a oggetti con indici può essere una modifica complessa e rivoluzionaria dell'app ed è più efficace per oggetti di grandi dimensioni con un numero elevato di riferimenti. Eseguire questa operazione solo se si notano tempi di raccolta dei rifiuti lunghi nell'applicazione correlati a oggetti con molti riferimenti.