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.
Questo articolo descrive i problemi relativi all'utilizzo di Garbage Collection e della memoria. Affronta i problemi che riguardano l'heap gestito e spiega come ridurre al minimo l'effetto della raccolta dei rifiuti sulle applicazioni. Ogni problema include collegamenti a procedure che è possibile usare per analizzare i problemi.
Strumenti di analisi delle prestazioni
Le sezioni seguenti descrivono gli strumenti disponibili per analizzare l'utilizzo della memoria e i problemi di Garbage Collection. Le procedure fornite più avanti in questo articolo fanno riferimento a questi strumenti.
Contatori delle prestazioni della memoria
È possibile usare i contatori delle prestazioni per raccogliere dati sulle prestazioni. Per istruzioni, vedere Profiling di runtime. La categoria Memoria CLR .NET dei contatori delle prestazioni, come descritto in Contatori delle prestazioni in .NET, fornisce informazioni sul Garbage Collector.
Debug con SOS
È possibile usare windows Debugger (WinDbg) per controllare gli oggetti nell'heap gestito.
Per installare WinDbg, installare Strumenti di debug per Windows dalla pagina Scarica strumenti di debug per Windows .
Eventi ETW di Garbage Collection
Event Tracing for Windows (ETW) è un sistema di traccia che integra il supporto di profilatura e debug fornito da .NET. A partire da .NET Framework 4, gli eventi ETW di garbage collection acquisiscono informazioni utili per l'analisi dell'heap gestito da un punto di vista statistico. Ad esempio, l'evento GCStart_V1
, generato quando si verifica un'operazione di Garbage Collection, fornisce le informazioni seguenti:
- Quale generazione di oggetti viene raccolta?
- Cosa ha attivato l'operazione di Garbage Collection.
- Tipo di raccolta dei rifiuti di memoria (concurrente o non concurrente).
La registrazione eventi ETW è efficiente e non maschera eventuali problemi di prestazioni associati a Garbage Collection. Un processo può fornire i propri eventi in combinazione con gli eventi ETW. Quando vengono registrati, sia gli eventi dell'applicazione che gli eventi di Garbage Collection possono essere correlati per determinare come e quando si verificano problemi dell'heap. Ad esempio, un'applicazione server può fornire eventi all'inizio e alla fine di una richiesta client.
L'API di profilazione
Le interfacce di profilatura CLR (Common Language Runtime) forniscono informazioni dettagliate sugli oggetti interessati durante la Garbage Collection. Un profiler può ricevere una notifica quando una raccolta dei rifiuti inizia e finisce. Può fornire report sugli oggetti nell'heap gestito, inclusa l'identificazione di oggetti in ogni generazione. Per altre informazioni, vedere Panoramica della profilatura.
I profiler possono fornire informazioni complete. Tuttavia, i profiler complessi possono potenzialmente modificare il comportamento di un'applicazione.
Monitoraggio delle risorse del dominio applicazione
A partire da .NET Framework 4, il monitoraggio delle risorse del dominio applicazione consente agli host di monitorare l'utilizzo della CPU e della memoria in base al dominio applicazione. Per altre informazioni, vedere Application Domain Resource Monitoring.
Risolvere i problemi di prestazioni
Il primo passaggio consiste nel determinare se il problema è effettivamente Garbage Collection. Se si determina che è necessario, scegliere dall'elenco seguente per risolvere il problema.
- Viene generata un'eccezione di memoria insufficiente
- Il processo usa una quantità eccessiva di memoria
- Il Garbage Collector non recupera oggetti abbastanza velocemente
- L'heap gestito è troppo frammentato
- Le pause di Garbage Collection sono troppo lunghe
- La generazione 0 è troppo grande
- L'utilizzo della CPU durante un'operazione di Garbage Collection è troppo elevato
Problema: viene generata un'eccezione di memoria insufficiente
Esistono due casi legittimi per generare un'eccezione gestita OutOfMemoryException :
Memoria virtuale insufficiente.
Il Garbage Collector alloca memoria dal sistema in segmenti di una dimensione predefinita. Se un'allocazione richiede un segmento aggiuntivo, ma non esiste un blocco libero contiguo lasciato nello spazio di memoria virtuale del processo, l'allocazione per l'heap gestito avrà esito negativo.
Non c'è abbastanza memoria fisica da allocare.
Se si determina che l'eccezione non è legittima, contattare il servizio clienti Microsoft e il supporto tecnico con le informazioni seguenti:
- Stack con l'eccezione gestita di esaurimento della memoria.
- Dump completo della memoria.
- Dati che dimostrano che non si tratta di un'eccezione legittima di memoria insufficiente, inclusi i dati che mostrano che la memoria virtuale o fisica non è un problema.
Problema: il processo usa una quantità eccessiva di memoria
Un presupposto comune è che l'utilizzo della memoria visualizzato nella scheda Prestazioni di Gestione attività di Windows può indicare quando viene usata una quantità eccessiva di memoria. Tuttavia, tale visualizzazione riguarda il working set; non fornisce informazioni sull'utilizzo della memoria virtuale.
Se si determina che il problema è causato dall'heap gestito, è necessario misurare l'heap gestito nel tempo per determinare eventuali modelli.
Se si determina che il problema non è causato dall'heap gestito, è necessario usare il debug nativo.
Problema: Il Garbage Collector non recupera oggetti abbastanza velocemente
Quando sembra che gli oggetti non vengano rilasciati come previsto per la garbage collection, è necessario determinare se sono presenti riferimenti forti a tali oggetti.
È anche possibile che si verifichi questo problema se non è stata eseguita alcuna Garbage Collection per la generazione che contiene un oggetto morto, che indica che il finalizzatore per l'oggetto non è stato eseguito. Ad esempio, ciò è possibile quando si esegue un'applicazione in un appartamento a thread singolo (STA) e il thread che gestisce la coda del finalizzatore non può accedervi.
Controlli delle prestazioni |
---|
Controllare i riferimenti agli oggetti. Verificare se un finalizzatore è stato eseguito. Determinare se sono presenti oggetti in attesa di essere finalizzati. |
Problema: l'heap gestito è troppo frammentato
Il livello di frammentazione viene calcolato come rapporto di spazio libero rispetto alla memoria totale allocata per la generazione. Per la generazione 2, un livello di frammentazione accettabile non è superiore a 20%. Poiché la generazione 2 può diventare molto grande, il rapporto di frammentazione è più importante del valore assoluto.
La presenza di un sacco di spazio libero nella generazione 0 non è un problema perché si tratta della generazione in cui vengono allocati nuovi oggetti.
La frammentazione si verifica sempre nell'heap di oggetti di grandi dimensioni perché non è compattata. Gli oggetti liberi adiacenti vengono naturalmente compressi in un unico spazio per soddisfare richieste di allocazione di oggetti di grandi dimensioni.
La frammentazione può diventare un problema nella generazione 1 e nella generazione 2. Se queste generazioni hanno una grande quantità di spazio disponibile dopo un'operazione di Garbage Collection, l'utilizzo degli oggetti di un'applicazione potrebbe richiedere modifiche ed è consigliabile valutare nuovamente la durata degli oggetti a lungo termine.
Il bloccaggio eccessivo di oggetti può aumentare la frammentazione. Se la frammentazione è elevata, troppi oggetti potrebbero essere stati bloccati.
Se la frammentazione della memoria virtuale impedisce al Garbage Collector di aggiungere segmenti, le cause potrebbero essere una delle seguenti:
Caricamento e scaricamento frequente di molti piccoli assemblaggi.
Contiene troppi riferimenti agli oggetti COM durante l'interoperabilità con codice non gestito.
Creazione di oggetti transitori di grandi dimensioni, che causa frequentemente l'allocazione e la liberazione di segmenti di heap di grandi dimensioni.
Quando si ospita CLR, un'applicazione può richiedere che il Garbage Collector mantenga i segmenti. In questo modo si riduce la frequenza delle allocazioni di segmenti. A tale scopo, usare il flag STARTUP_HOARD_GC_VM nell'enumerazione STARTUP_FLAGS.
Controlli delle prestazioni |
---|
Determinare la quantità di spazio disponibile nell'heap gestito. Determinare il numero di oggetti bloccati. |
Se si ritiene che non vi sia alcuna causa legittima per la frammentazione, contattare il servizio clienti Microsoft e il supporto tecnico.
Problema: Le pause di Garbage Collection sono troppo lunghe
La raccolta dei rifiuti opera in tempo reale moderato, pertanto, un'applicazione deve essere capace di tollerare alcune pause. Un criterio per il tempo reale flessibile è che 95% delle operazioni devono terminare in tempo.
In Garbage Collection simultanei, i thread gestiti possono essere eseguiti durante una raccolta, il che significa che le pause sono molto minime.
Le raccolte di rifiuti effimeri (generazioni 0 e 1) durano solo pochi millisecondi, quindi ridurre le pause di solito non riesce. Tuttavia, è possibile ridurre le pause nelle raccolte di seconda generazione modificando il modello di richieste di allocazione da parte di un'applicazione.
Un altro metodo, più accurato, consiste nell'usare gli eventi ETW di Garbage Collection. È possibile trovare i tempi per le raccolte aggiungendo le differenze di timestamp per una sequenza di eventi. L'intera sequenza di raccolta include la sospensione del motore di esecuzione, la raccolta stessa e la ripresa del motore di esecuzione.
È possibile usare le notifiche di Garbage Collection per determinare se un server sta per avere una raccolta di seconda generazione e se reindirizzare le richieste a un altro server potrebbe risolvere eventuali problemi con le pause.
Controlli delle prestazioni |
---|
Determinare il periodo di tempo in un'operazione di Garbage Collection. Determinare la causa di un'operazione di Garbage Collection. |
Problema: la generazione 0 è troppo grande
È probabile che la generazione 0 abbia un numero maggiore di oggetti in un sistema a 64 bit, soprattutto quando si usa Garbage Collection del server invece di Garbage Collection della workstation. Ciò è dovuto al fatto che la soglia per attivare un'operazione di Garbage Collection di generazione 0 è superiore in questi ambienti e le raccolte di generazione 0 possono aumentare notevolmente. Le prestazioni vengono migliorate quando un'applicazione alloca più memoria prima dell'attivazione di un'operazione di Garbage Collection.
Problema: l'utilizzo della CPU durante un'operazione di Garbage Collection è troppo elevato
L'utilizzo della CPU sarà elevato durante un'operazione di Garbage Collection. Se una quantità significativa di tempo di processo viene impiegato in un'operazione di Garbage Collection, il numero di raccolte è troppo frequente o la raccolta dura troppo a lungo. Un aumento della frequenza di allocazione degli oggetti nell'heap gestito causa un'operazione di Garbage Collection più frequente. Riducendo il tasso di allocazione, si riduce la frequenza della Garbage Collection.
È possibile monitorare i tassi di allocazione usando il Allocated Bytes/second
contatore delle prestazioni. Per altre informazioni, vedere Contatori delle prestazioni in .NET.
La durata di una raccolta è principalmente un fattore del numero di oggetti che sopravvivono dopo l'allocazione. Il Garbage Collector deve passare attraverso una grande quantità di memoria se molti oggetti rimangono da raccogliere. Il lavoro per compattare i sopravvissuti richiede molto tempo. Per determinare il numero di oggetti gestiti durante una raccolta, impostare un punto di interruzione nel debugger alla fine di un'operazione di Garbage Collection per una generazione specificata.
Controlli delle prestazioni |
---|
Determinare se l'utilizzo elevato della CPU è causato dalla raccolta dei rifiuti. Impostare un punto di interruzione alla fine di Garbage Collection. |
Linee guida per la risoluzione dei problemi
Questa sezione descrive le linee guida da considerare durante l'avvio delle indagini.
Raccolta dei rifiuti della workstation o del server
Determinare se si sta usando il tipo corretto di raccolta dei rifiuti. Se l'applicazione usa più thread e istanze di oggetti, usare Garbage Collection del server invece di Garbage Collection della workstation. Il Garbage Collection del server opera su più thread, mentre la Garbage Collection della workstation richiede più istanze di un'applicazione per eseguire i propri thread di Garbage Collection e competere per il tempo della CPU.
Un'applicazione con un carico ridotto e che esegue raramente attività in background, ad esempio un servizio, potrebbe usare workstation garbage collection con garbage collection simultaneo disabilitato.
Quando misurare le dimensioni dell'heap gestito
A meno che non si usi un profiler, è necessario stabilire un modello di misurazione coerente per diagnosticare in modo efficace i problemi di prestazioni. Considerare i punti seguenti per stabilire una pianificazione:
- Se si misura dopo un'operazione di Garbage Collection di seconda generazione, l'intero heap gestito sarà privo di garbage (oggetti inattivi).
- Se si misura immediatamente dopo un'operazione di Garbage Collection di generazione 0, gli oggetti nelle generazioni 1 e 2 non verranno ancora raccolti.
- Se si misura immediatamente prima di una Garbage Collection, si potranno misurare quante più allocazioni possibili prima dell'avvio della Garbage Collection.
- La misurazione durante una raccolta dei rifiuti è problematica perché le strutture dati del lettore dei rifiuti non sono in un stato valido per essere attraversate e potrebbero non essere in grado di fornire risultati completi. Questo è intenzionale.
- Quando si utilizza la raccolta dei rifiuti della workstation con la raccolta dei rifiuti simultanea, gli oggetti recuperati non vengono compattati, quindi le dimensioni dell'heap possono essere uguali o maggiori (la frammentazione può far sembrare più grandi le dimensioni).
- La Garbage Collection simultanea alla generazione 2 viene ritardata quando il carico di memoria fisica è troppo elevato.
La procedura seguente descrive come impostare un punto di interruzione in modo da poter misurare l'heap gestito.
Per impostare un punto di interruzione alla fine della raccolta dei rifiuti
In WinDbg con l'estensione del debugger SOS caricata immettere il comando seguente:
bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"
Impostare
GcCondemnedGeneration
sulla generazione desiderata. Questo comando richiede simboli privati.Questo comando forza un'interruzione se
RestartEE
viene eseguita dopo che gli oggetti di seconda generazione sono stati recuperati per l'operazione di Garbage Collection.Nella raccolta dei rifiuti del server, un solo thread chiama
RestartEE
quindi il breakpoint verrà eseguito una sola volta durante una raccolta dei rifiuti di seconda generazione.
Procedure di controllo delle prestazioni
In questa sezione vengono descritte le procedure seguenti per isolare la causa del problema di prestazioni:
- Determinare se il problema è causato da Garbage Collection.
- Determinare se l'eccezione di memoria insufficiente è gestita.
- Determinare la quantità di memoria virtuale che può essere riservata.
- Determinare se la memoria fisica è sufficiente.
- Determinare la quantità di memoria impegnata dell'heap gestito.
- Determinare la quantità di memoria riservata dall'heap gestito.
- Determinare oggetti di grandi dimensioni nella generazione 2.
- Determinare i riferimenti agli oggetti.
- Verificare se un finalizzatore è stato eseguito.
- Determinare se sono presenti oggetti in attesa di essere finalizzati.
- Determinare la quantità di spazio disponibile nell'heap gestito.
- Determinare il numero di oggetti bloccati.
- Determinare il periodo di tempo in un'operazione di Garbage Collection.
- Determinare cosa ha causato un'operazione di Garbage Collection.
- Verificare se l'utilizzo elevato della CPU è causato dalla raccolta dei rifiuti.
Per determinare se il problema è causato da Garbage Collection
Esaminare i due contatori delle prestazioni di memoria seguenti:
% Ora nel GC. Visualizza la percentuale di tempo trascorso dedicato all'esecuzione di una garbage collection dopo l'ultimo ciclo di raccolta. Usare questo contatore per determinare se il raccoglitore di immondizia sta spendendo troppo tempo per rendere disponibile lo spazio dell'heap gestito. Se il tempo impiegato in garbage collection è relativamente basso, questo potrebbe indicare un problema di risorse all'esterno dell'heap gestito. Questo contatore potrebbe non essere accurato quando è coinvolta un'operazione di Garbage Collection simultanea o in background.
# Totale byte di cui è stato eseguito il commit. Visualizza la quantità di memoria virtuale attualmente sottoposta a commit dal Garbage Collector. Usare questo contatore per determinare se la memoria utilizzata dal Garbage Collector è una parte eccessiva della memoria usata dall'applicazione.
La maggior parte dei contatori delle prestazioni di memoria viene aggiornata alla fine di ogni Garbage Collection. Pertanto, potrebbero non riflettere le condizioni correnti su cui si desiderano informazioni.
Per determinare se l'eccezione di memoria insufficiente è gestita
Nel debugger WinDbg o Visual Studio con l'estensione debugger SOS caricata, immettere il comando print exception (
pe
):!pe
Se l'eccezione è gestita, OutOfMemoryException viene visualizzata come tipo di eccezione, come illustrato nell'esempio seguente.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):
Se l'output non specifica un'eccezione, è necessario determinare il thread da cui proviene l'eccezione di memoria insufficiente. Immettere il comando seguente nel debugger per visualizzare tutti i thread con i relativi stack di chiamate:
~\*kb
Il thread dello stack contenente chiamate di eccezione è indicato dall'argomento
RaiseTheException
. Questo è l'oggetto di eccezione gestito.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0
È possibile usare il comando seguente per eseguire il dump delle eccezioni annidate.
!pe -nested
Se non vengono trovate eccezioni, l'eccezione di esaurimento della memoria ha avuto origine da codice non gestito.
Per determinare la quantità di memoria virtuale che può essere riservata
In WinDbg con l'estensione del debugger SOS caricata immettere il comando seguente per ottenere l'area gratuita più grande:
!address -summary
L'area gratuita più grande viene visualizzata come illustrato nell'output seguente.
Largest free region: Base 54000000 - Size 0003A980
In questo esempio, la dimensione dell'area libera più grande è di circa 24000 KB (3A980 in esadecimale). Questa area è molto più piccola rispetto a quanto richiesto dal Garbage Collector per un segmento.
-o-
Usare il comando
vmstat
:!vmstat
L'area libera più grande è il valore più grande nella colonna MAXIMUM, come illustrato nell'output seguente.
TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~~ ~~~~ Free: Small 8K 64K 46K 36 1,671K Medium 80K 864K 349K 3 1,047K Large 1,384K 1,278,848K 151,834K 12 1,822,015K Summary 8K 1,278,848K 35,779K 51 1,824,735K
Per determinare se la memoria fisica è sufficiente
Avvia Gestione attività di Windows.
Sulla scheda
Performance
, esamina il valore impegnato. (In Windows 7, consultareCommit (KB)
inSystem group
.)Total
Se è vicino aLimit
, la memoria fisica è insufficiente.
Per determinare la quantità di memoria impegnata dell'heap gestito
Usare il
# Total committed bytes
contatore delle prestazioni della memoria per determinare il numero di byte che l'heap gestito sta impegnando. Il Garbage Collector esegue il commit di blocchi in un segmento in base alle esigenze, non tutti contemporaneamente.Annotazioni
Non usare il
# Bytes in all Heaps
contatore delle prestazioni, perché non rappresenta l'utilizzo effettivo della memoria dall'heap gestito. La dimensione di una generazione è inclusa in questo valore ed è effettivamente la dimensione soglia, ovvero la dimensione che induce una raccolta di rifiuti se la generazione viene riempita di oggetti. Pertanto, questo valore è in genere zero.
Per determinare la quantità di memoria riservata dall'heap gestito
Utilizza il contatore delle prestazioni della memoria
# Total reserved bytes
.Il Garbage Collector riserva memoria in segmenti ed è possibile determinare dove inizia un segmento usando il
eeheap
comando .Importante
Sebbene sia possibile determinare la quantità di memoria allocata dal Garbage Collector per ogni segmento, le dimensioni del segmento sono specifiche dell'implementazione e sono soggette a modifiche in qualsiasi momento, inclusi gli aggiornamenti periodici. L'app non deve mai fare ipotesi su o dipendere da una determinata dimensione del segmento, né deve tentare di configurare la quantità di memoria disponibile per le allocazioni dei segmenti.
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente:
!eeheap -gc
Il risultato è il seguente.
Number of GC Heaps: 2 ------------------------------ Heap 0 (002db550) generation 0 starts at 0x02abe29c generation 1 starts at 0x02abdd08 generation 2 starts at 0x02ab0038 ephemeral segment allocation context: none segment begin allocated size 02ab0000 02ab0038 02aceff4 0x0001efbc(126908) Large object heap starts at 0x0aab0038 segment begin allocated size 0aab0000 0aab0038 0aab2278 0x00002240(8768) Heap Size 0x211fc(135676) ------------------------------ Heap 1 (002dc958) generation 0 starts at 0x06ab1bd8 generation 1 starts at 0x06ab1bcc generation 2 starts at 0x06ab0038 ephemeral segment allocation context: none segment begin allocated size 06ab0000 06ab0038 06ab3be4 0x00003bac(15276) Large object heap starts at 0x0cab0038 segment begin allocated size 0cab0000 0cab0038 0cab0048 0x00000010(16) Heap Size 0x3bbc(15292) ------------------------------ GC Heap Size 0x24db8(150968)
Gli indirizzi indicati da "segmento" sono gli indirizzi iniziali dei segmenti.
Per determinare oggetti di grandi dimensioni nella generazione 2
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente:
!dumpheap –stat
Se l'heap gestito è grande,
dumpheap
potrebbe richiedere un po' di tempo per completarsi.È possibile iniziare ad analizzare le ultime righe dell'output, perché elencano gli oggetti che usano il maggior numero di spazio. Per esempio:
2c6108d4 173712 14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo 00155f80 533 15216804 Free 7a747c78 791070 15821400 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700930 19626040 System.Collections.Specialized.ListDictionary 2c64e36c 78644 20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo 79124228 121143 29064120 System.Object[] 035f0ee4 81626 35588936 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 40182 90664128 System.Collections.Hashtable+bucket[] 790fa3e0 3154024 137881448 System.String Total 8454945 objects
L'ultimo oggetto elencato è una stringa e occupa la maggior parte dello spazio. È possibile esaminare l'applicazione per vedere come è possibile ottimizzare gli oggetti stringa. Per visualizzare stringhe comprese tra 150 e 200 byte, immettere quanto segue:
!dumpheap -type System.String -min 150 -max 200
Di seguito è riportato un esempio dei risultati.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …
L'uso di un numero intero invece di una stringa per un ID può essere più efficiente. Se la stessa stringa viene ripetuta migliaia di volte, prendere in considerazione l'internamento delle stringhe. Per ulteriori informazioni sull'internamento delle stringhe, vedere l'argomento di riferimento per il String.Intern metodo.
Per determinare i riferimenti agli oggetti
In WinDbg con l'estensione del debugger SOS caricata immettere il comando seguente per elencare i riferimenti agli oggetti:
!gcroot
-o-
Per determinare i riferimenti per un oggetto specifico, includere l'indirizzo:
!gcroot 1c37b2ac
Le radici trovate negli stack possono risultare falsi positivi. Per altre informazioni, usare il comando
!help gcroot
.ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)-> 19010b78(DemoApp.FormDemoApp)-> 19011158(System.Windows.Forms.PropertyStore)-> … [omitted] 1c3745ec(System.Data.DataTable)-> 1c3747a8(System.Data.DataColumnCollection)-> 1c3747f8(System.Collections.Hashtable)-> 1c376590(System.Collections.Hashtable+bucket[])-> 1c376c98(System.Data.DataColumn)-> 1c37b270(System.Data.Common.DoubleStorage)-> 1c37b2ac(System.Double[]) Scan Thread 0 OSTHread 99c Scan Thread 6 OSTHread 484
Il completamento del
gcroot
comando può richiedere molto tempo. Qualsiasi oggetto non recuperato da Garbage Collection è un oggetto attivo. Ciò significa che una radice è direttamente o indirettamente in possesso dell'oggetto, pertantogcroot
deve restituire le informazioni sul percorso all'oggetto. È consigliabile esaminare i grafici restituiti e verificare il motivo per cui questi oggetti sono ancora a cui si fa riferimento.
Per determinare se è stato eseguito un finalizzatore
Eseguire un programma di test contenente il codice seguente:
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Se il test risolve il problema, significa che il Garbage Collector non recuperava oggetti, perché i finalizzatori per tali oggetti erano stati sospesi. Il GC.WaitForPendingFinalizers metodo consente ai finalizzatori di completare le attività e risolve il problema.
Per determinare se sono presenti oggetti in attesa di essere finalizzati
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente:
!finalizequeue
Esaminare il numero di oggetti pronti per la finalizzazione. Se il numero è elevato, è necessario esaminare il motivo per cui questi finalizzatori non possono progredire affatto o non possono progredire abbastanza velocemente.
Per ottenere un output dei thread, immettere il comando seguente:
!threads -special
Questo comando fornisce un output simile al seguente.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEE
Il thread del finalizzatore indica se è in esecuzione un finalizzatore, e in tal caso quale. Quando un thread finalizzatore non esegue alcun finalizzatore, è in attesa di un evento che gli indichi di svolgere il proprio lavoro. Nella maggior parte dei casi, il thread finalizzatore verrà visualizzato in questo stato perché viene eseguito in THREAD_HIGHEST_PRIORITY e dovrebbe terminare l'esecuzione dei finalizzatori, se presenti, molto rapidamente.
Per determinare la quantità di spazio disponibile nell'heap gestito
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente:
!dumpheap -type Free -stat
Questo comando visualizza le dimensioni totali di tutti gli oggetti liberi nell'heap gestito, come illustrato nell'esempio seguente.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objects
Per determinare lo spazio disponibile nella generazione 0, immettere il comando seguente per informazioni sull'utilizzo della memoria per generazione:
!eeheap -gc
Questo comando visualizza un output simile al seguente. L'ultima riga mostra il segmento temporaneo.
Heap 0 (0015ad08) generation 0 starts at 0x49521f8c generation 1 starts at 0x494d7f64 generation 2 starts at 0x007f0038 ephemeral segment allocation context: none segment begin allocated size 00178250 7a80d84c 7a82f1cc 0x00021980(137600) 00161918 78c50e40 78c7056c 0x0001f72c(128812) 007f0000 007f0038 047eed28 0x03ffecf0(67103984) 3a120000 3a120038 3a3e84f8 0x002c84c0(2917568) 46120000 46120038 49e05d04 0x03ce5ccc(63855820)
Calcolare lo spazio usato dalla generazione 0:
? 49e05d04-0x49521f8c
Il risultato è il seguente. La generazione 0 è di circa 9 MB.
Evaluate expression: 9321848 = 008e3d78
Il comando seguente esegue il dump dello spazio disponibile all'interno dell'intervallo di generazione 0:
!dumpheap -type Free -stat 0x49521f8c 49e05d04
Il risultato è il seguente.
------------------------------ Heap 0 total 409 objects ------------------------------ Heap 1 total 0 objects ------------------------------ Heap 2 total 0 objects ------------------------------ Heap 3 total 0 objects ------------------------------ total 409 objects Statistics: MT Count TotalSize Class Name 0015a498 409 7296540 Free Total 409 objects
Questo output mostra che la parte di generazione 0 dell'heap usa 9 MB di spazio per gli oggetti e ha 7 MB gratuiti. Questa analisi mostra la misura in cui la generazione 0 contribuisce alla frammentazione. Questa quantità di utilizzo dell'heap deve essere ridotta dall'importo totale come causa della frammentazione causata da oggetti a lungo termine.
Per determinare il numero di oggetti fissati
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente:
!gchandles
Le statistiche visualizzate includono il numero di handle fissati, come illustrato nell'esempio seguente.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Per determinare il periodo di tempo in un'operazione di Garbage Collection
Esaminare il contatore delle prestazioni della
% Time in GC
memoria.Il valore viene calcolato usando un tempo di intervallo di campionamento. Poiché i contatori vengono aggiornati alla fine di ogni Garbage Collection, l'esempio corrente avrà lo stesso valore dell'esempio precedente se non si sono verificate raccolte durante l'intervallo.
Il tempo di raccolta viene ottenuto moltiplicando l'intervallo di campionamento con il valore percentuale.
I dati seguenti mostrano quattro intervalli di campionamento di due secondi, per uno studio di 8 secondi. Le colonne
Gen0
,Gen1
, eGen2
mostrano il numero totale di raccolte dei rifiuti completato entro la fine dell'intervallo per quella generazione.Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 10 2 10 3 1 1 3 11 3 1 3 4 11 3 1 3
Queste informazioni non mostrano quando si è verificata la garbage collection, ma è possibile determinare quante garbage collection si sono verificate in un intervallo di tempo. Supponendo il caso peggiore, la decima generazione 0 garbage collection è terminata all'inizio del secondo intervallo e l'undicesima generazione 0 garbage collection è stata completata alla fine del terzo intervallo. Il tempo tra la fine della decima e la fine dell'undicesima raccolta dei rifiuti è di circa 2 secondi, e il contatore delle prestazioni mostra 3%, quindi la durata della raccolta dei rifiuti di generazione 0 dell'undicesima era (2 secondi * 3% = 60ms).
Nell'esempio seguente sono presenti cinque intervalli.
Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 3 2 10 3 1 1 3 11 4 1 1 4 11 4 1 1 5 11 4 2 20
La garbage collection di seconda generazione è iniziata durante il quarto intervallo ed è stata completata durante il quinto intervallo. Supponendo che il caso peggiore, l'ultima operazione di Garbage Collection fosse per una raccolta di generazione 0 terminata all'inizio del terzo intervallo e l'operazione di Garbage Collection di seconda generazione è stata completata alla fine del quinto intervallo. Di conseguenza, il tempo compreso tra la fine della garbage collection di generazione 0 e la fine della Garbage Collection di seconda generazione è di 4 secondi. Poiché il
% Time in GC
contatore è 20%, il tempo massimo che la garbage collection di generazione 2 potrebbe aver impiegato è (4 secondi * 20% = 800ms).In alternativa, è possibile determinare la lunghezza di un'operazione di Garbage Collection usando gli eventi ETW di Garbage Collection e analizzare le informazioni per determinare la durata di Garbage Collection.
Ad esempio, i dati seguenti mostrano una sequenza di eventi che si è verificata durante un'operazione di Garbage Collection non simultanea.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEnd
La sospensione del thread gestito ha richiesto 26 microsecondi (
GCSuspendEEEnd
–GCSuspendEEBegin_V1
).La raccolta dei rifiuti effettiva ha richiesto 4,8 ms (
GCEnd_V1
–GCStart_V1
).La ripresa dei thread gestiti ha richiesto 21 us (
GCRestartEEEnd
–GCRestartEEBegin
).L'output seguente fornisce un esempio di raccolta dei rifiuti in background e include i campi processo, thread e campi degli eventi. Non vengono visualizzati tutti i dati.
timestamp(us) event name process thread event field 42504385 GCSuspendEEBegin_V1 Test.exe 4372 1 42504648 GCSuspendEEEnd Test.exe 4372 42504816 GCStart_V1 Test.exe 4372 102019 42504907 GCStart_V1 Test.exe 4372 102020 42514170 GCEnd_V1 Test.exe 4372 42514204 GCHeapStats Test.exe 4372 102020 42832052 GCRestartEEBegin Test.exe 4372 42832136 GCRestartEEEnd Test.exe 4372 63685394 GCSuspendEEBegin_V1 Test.exe 4744 6 63686347 GCSuspendEEEnd Test.exe 4744 63784294 GCRestartEEBegin Test.exe 4744 63784407 GCRestartEEEnd Test.exe 4744 89931423 GCEnd_V1 Test.exe 4372 102019 89931464 GCHeapStats Test.exe 4372
L'evento
GCStart_V1
a 42504816 indica che si tratta di una raccolta dei rifiuti in background, perché l'ultimo campo è1
. Questo diventa Garbage Collection No. 102019.L'evento
GCStart
si verifica perché è necessaria una raccolta dei rifiuti effimera prima di avviare una raccolta dei rifiuti in background. Questa operazione si trasforma nella raccolta dei rifiuti numero 102020.Alle ore 42.514.170, il processo di raccolta rifiuti n. 102020 termina. I thread gestiti vengono riavviati a questo punto. Questa operazione è stata completata nel thread 4372, che ha attivato la Garbage Collection in background.
Nel thread 4744 si verifica una sospensione. Questo è l'unico momento in cui la raccolta dei rifiuti in background deve sospendere i thread gestiti. Questa durata è di circa 99 ms ((63784407-63685394)/1000).
L'evento
GCEnd
per l'operazione di Garbage Collection in background si trova in 89931423. Ciò significa che la Garbage Collection in background è durata circa 47 secondi ((89931423-42504816)/1000).È possibile visualizzare qualsiasi numero di raccolte di spazzatura temporanee mentre i thread gestiti sono in esecuzione.
Per determinare l'attivazione di un'operazione di Garbage Collection
Nel debugger WinDbg o Visual Studio con l'estensione del debugger SOS caricata immettere il comando seguente per visualizzare tutti i thread con i relativi stack di chiamate:
~*KB
Questo comando visualizza un output simile al seguente.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48
Se la Garbage Collection è stata causata da una notifica di memoria insufficiente dal sistema operativo, lo stack di chiamate è simile, ad eccezione del fatto che il thread è il thread del finalizzatore. Il thread finalizzatore riceve una notifica asincrona di bassa memoria e avvia la raccolta dei rifiuti.
Se l'operazione di Garbage Collection è stata causata dall'allocazione di memoria, lo stack viene visualizzato come segue:
0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration 0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1 0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18 0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b 0012f310 7a02ae4c mscorwks!Alloc+0x60 0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd 0012f424 300027f4 mscorwks!JIT_NewArr1+0x148 000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c 0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153
Un assistente just-in-time (
JIT_New*
) alla fine chiamaGCHeap::GarbageCollectGeneration
. Se si determina che le collezioni di garbage di seconda generazione sono causate dalle allocazioni, è necessario identificare quali oggetti vengono raccolti da una collezione di garbage di seconda generazione e come evitarli. Ciò significa che si desidera determinare la differenza tra l'inizio e la fine di una raccolta di seconda generazione e gli oggetti che hanno causato questa raccolta di seconda generazione.Ad esempio, immettere il comando seguente nel debugger per visualizzare l'inizio di una raccolta di seconda generazione:
!dumpheap –stat
Output di esempio (abridged per mostrare gli oggetti che usano la maggior parte dello spazio):
79124228 31857 9862328 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 00155f80 21248 12256296 Free 79103b6c 297003 13068132 System.Threading.ReaderWriterLock 7a747ad4 708732 14174640 System.Collections.Specialized.HybridDictionary 7a747c78 786498 15729960 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 035f0ee4 89192 38887712 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 7912c444 91616 71887080 System.Double[] 791242ec 32451 82462728 System.Collections.Hashtable+bucket[] 790fa3e0 2459154 112128436 System.String Total 6471774 objects
Ripetere il comando alla fine della generazione 2:
!dumpheap –stat
Output di esempio (abridged per mostrare gli oggetti che usano la maggior parte dello spazio):
79124228 26648 9314256 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 79103b6c 296770 13057880 System.Threading.ReaderWriterLock 7a747ad4 708730 14174600 System.Collections.Specialized.HybridDictionary 7a747c78 786497 15729940 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 00155f80 13806 34007212 Free 035f0ee4 89187 38885532 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 32370 82359768 System.Collections.Hashtable+bucket[] 790fa3e0 2440020 111341808 System.String Total 6417525 objects
Gli
double[]
oggetti sono scomparsi dalla fine dell'output, il che significa che sono stati raccolti. Questi oggetti rappresentano circa 70 MB. Gli oggetti rimanenti non cambiavano molto. Pertanto, questi oggettidouble[]
erano il motivo per cui si è verificata questa raccolta di generazione 2. Il passaggio successivo consiste nel determinare perché glidouble[]
oggetti sono presenti e perché sono morti. È possibile chiedere allo sviluppatore di codice da cui provengono questi oggetti oppure è possibile usare ilgcroot
comando .
Per determinare se l'utilizzo elevato della CPU è causato dalla raccolta dei dati inutili
Correlare il valore del
% Time in GC
contatore delle prestazioni della memoria con l'ora del processo.Se il
% Time in GC
valore aumenta contemporaneamente al tempo di elaborazione, l'operazione di Garbage Collection causa un utilizzo elevato della CPU. In caso contrario, profilare l'applicazione per individuare la posizione in cui si verifica l'utilizzo elevato.