Misurare l'utilizzo della memoria in Visual Studio (C#, Visual Basic, C++, F#)

È possibile rilevare perdite di memoria e memoria inefficiente mentre si sta eseguendo il debug con lo strumento di diagnostica Utilizzo memoria integrato nel debugger. Lo strumento Utilizzo memoria consente di eseguire uno o più snapshot dell'heap di memoria gestito e nativo per comprendere meglio l'impatto sull'utilizzo della memoria dei tipi di oggetti. È anche possibile analizzare l'utilizzo della memoria senza un debugger collegato o specificando come destinazione un'app in esecuzione. Per altre informazioni, vedere Eseguire gli strumenti di profilatura con o senza il debugger. Per informazioni sulla scelta dello strumento di analisi della memoria migliore per le proprie esigenze, vedere Scegliere uno strumento di analisi della memoria.

Anche se è possibile raccogliere snapshot di memoria in qualsiasi momento nello strumento Utilizzo memoria è possibile usare il debugger di Visual Studio per controllare la modalità di esecuzione dell'applicazione durante l'analisi dei problemi di prestazioni. L'impostazione dei punti di interruzione, l'esecuzione di istruzioni, l'azione Interrompi tutto e altre azioni del debugger consentono di concentrare l'analisi delle prestazioni sui percorsi del codice più rilevanti. L'esecuzione di tali azioni durante l'esecuzione dell'app può eliminare il rumore dal codice che non interessa l'utente e può ridurre notevolmente la quantità di tempo necessaria per la diagnosi di un problema.

Importante

Gli strumenti di diagnostica integrati nel debugger sono supportati per lo sviluppo .NET in Visual Studio, tra cui ASP.NET, ASP.NET Core, sviluppo nativo/C++ e app in modalità mista (.NET e native). Per Windows 8 e versioni successive è necessario eseguire gli strumenti di profilatura con il debugger, nella finestra Strumenti di diagnostica.

Questa esercitazione illustra come:

  • Creare snapshot della memoria
  • Analizzare i dati di utilizzo della memoria

Se l'utilizzo della memoria non fornisce i dati necessari, altri strumenti di profilatura nel profiler prestazioni forniscono diversi tipi di informazioni che potrebbero essere utili per l'utente. In molti casi, il collo di bottiglia delle prestazioni dell'applicazione può essere causato da un elemento diverso dalla memoria, ad esempio CPU, interfaccia utente di rendering o tempo di richiesta di rete.

Nota

Supporto dell'allocatore personalizzato Il profiler di memoria nativa funziona raccogliendo i dati dell'evento ETW di allocazione generati durante l'esecuzione. Gli allocatori in CRT e Windows SDK sono stati annotati a livello di origine in modo che sia possibile acquisirne i dati di allocazione. Nella scrittura degli allocatori, fare in modo che qualsiasi funzione che restituisce un puntatore alla memoria heap appena allocata possa essere decorata con __declspec(allocator), come illustrato in questo esempio per myMalloc:

__declspec(allocator) void* myMalloc(size_t size)

Raccogliere i dati sull'utilizzo della memoria

  1. Aprire il progetto per cui si vuole eseguire il debug in Visual Studio e impostare un punto di interruzione nell'app in corrispondenza del punto in cui si vuole iniziare a esaminare l'utilizzo della memoria.

    Se è presente un'area in cui si sospetta un problema di memoria, impostare il primo punto di interruzione prima che si verifichi tale problema.

    Suggerimento

    Poiché può essere difficile acquisire il profilo di memoria di un'operazione specifica quando l'app alloca e dealloca spesso la memoria, impostare punti di interruzione all'inizio e alla fine dell'operazione o eseguire i vari passaggi dell'operazione per trovare il punto esatto in cui l'utilizzo della memoria è cambiato.

  2. Impostare un secondo punto di interruzione alla fine della funzione o dell'area di codice da analizzare o dopo un problema di utilizzo sospetto della memoria.

  3. La finestra Strumenti di diagnostica viene visualizzata automaticamente, a meno che non sia stata disattivata. Per visualizzare di nuovo la finestra, fare clic su Debug>Finestre>Mostra strumenti di diagnostica.

  4. Scegliere Utilizzo memoria con l'impostazione Seleziona strumenti sulla barra degli strumenti.

    Screenshot of Diagnostics Tools.

    Screenshot of Diagnostics Tools.

  5. Fare clic su Debug / Avvia debug (o Avvia sulla barra degli strumenti o F5).

    Al termine del caricamento dell'applicazione viene visualizzata la vista Riepilogo degli strumenti di diagnostica.

    Screenshot of Diagnostics Tools Summary Tab.

    Nota

    Poiché la raccolta di dati può influire sulle prestazioni di debug delle app native o in modalità mista, gli snapshot di memoria sono disattivati per impostazione predefinita. Per abilitare gli snapshot in app native o in modalità mista, avviare una sessione di debug (tasto di scelta rapida: F5). Quando viene visualizzata la finestra Strumenti di diagnostica, scegliere la scheda Utilizzo memoria e quindi Profilatura heap.

    Screenshot of Enable snapshots.

    Arrestare con la combinazione di tasti MAIUSC+F5 e riavviare il debug.

    Screenshot of Diagnostics Tools Summary Tab.

    Nota

    Poiché la raccolta di dati può influire sulle prestazioni di debug delle app native o in modalità mista, gli snapshot di memoria sono disattivati per impostazione predefinita. Per abilitare gli snapshot in app native o in modalità mista, avviare una sessione di debug (tasto di scelta rapida: F5). Quando viene visualizzata la finestra Strumenti di diagnostica, scegliere la scheda Utilizzo memoria e quindi Profilatura heap.

    Screenshot of Enable snapshots.

    Arrestare con la combinazione di tasti MAIUSC+F5 e riavviare il debug.

  6. Per creare uno snapshot all'inizio della sessione di debug, scegliere Crea snapshot sulla barra degli strumenti di riepilogo Utilizzo memoria. Può essere utile impostare anche qui un punto di interruzione.

    Screenshot of Take Snapshot button.

    Screenshot of Take Snapshot button.

    Suggerimento

    Per creare una linea di base per i confronti di memoria, si consiglia di creare uno snapshot all'inizio di una sessione di debug.

  7. Eseguire lo scenario in cui viene raggiunto il primo punto di interruzione.

  8. Quando il debugger viene messo in pausa in corrispondenza del primo punto di interruzione, scegliere Crea snapshot sulla barra degli strumenti di riepilogo Utilizzo memoria.

  9. Premere F5 per eseguire l'applicazione fino al secondo punto di interruzione.

  10. A questo punto, creare un altro snapshot.

    A questo punto, è possibile iniziare ad analizzare i dati.

    In caso di problemi durante la raccolta o la visualizzazione dei dati, vedere Risolvere gli errori di profilatura e risolvere i problemi.

Analizzare i dati di utilizzo della memoria

Le righe della tabella di riepilogo Utilizzo memoria elencano gli snapshot acquisiti durante la sessione di debug e forniscono collegamenti a visualizzazioni più dettagliate.

Screenshot of Memory Usage table.

Screenshot of Memory Usage table.

Il nome della colonna dipende dalla modalità di debug scelta nelle proprietà del progetto: .NET, native o mixed (sia .NET che native).

  • Le colonne Oggetti (diff)e Allocazioni (diff) visualizzano il numero di oggetti nella memoria .NET e nativa quando è stato creato lo snapshot.

  • La colonna Dimensioni heap (diff) visualizza il numero di byte negli heap nativi e .NET

Quando si eseguono più snapshot, le celle della tabella di riepilogo includono la modifica del valore tra lo snapshot della riga e lo snapshot precedente.

Per analizzare l'utilizzo della memoria, fare clic su uno dei collegamenti che consente di visualizzare un report dettagliato dell'utilizzo della memoria:

  • Per visualizzare i dettagli della differenza tra lo snapshot corrente e lo snapshot precedente, scegliere il collegamento di modifica a sinistra della freccia (Memory Usage Increase). Una freccia rossa indica un aumento dell'utilizzo della memoria e una freccia verde indica una diminuzione.

Suggerimento

Per identificare i problemi di memoria più rapidamente, i report diff vengono ordinati in base ai tipi di oggetto che sono aumentati maggiormente in termini di numero (fare clic sul collegamento di modifica nella colonna Oggetti (diff)) o di dimensioni complessive dell'heap (fare clic sul collegamento di modifica nella colonna Dimensioni heap (diff)).

  • Per visualizzare i dettagli relativi solo allo snapshot selezionato, fare clic sul collegamento non di modifica.

    Il report verrà visualizzato in una finestra separata.

Report di tipi gestiti

Scegliere il collegamento corrente di una cella Oggetti (Diff) o Allocazioni (Diff) nella tabella di riepilogo Utilizzo memoria.

Screenshot of managed type report.

Screenshot of managed type report.

Il riquadro superiore mostra il numero e la dimensione dei tipi dello snapshot, inclusa la dimensione di tutti gli oggetti cui fa riferimento il tipo (Dimensione inclusiva).

L'albero Percorsi della radice del riquadro inferiore mostra gli oggetti che fanno riferimento al tipo selezionato nel riquadro superiore. .NET Garbage Collector pulisce la memoria per un oggetto solo quando l'ultimo tipo a cui fa riferimento è stato rilasciato.

Nell'albero Oggetti a cui si fa riferimento vengono visualizzati i riferimenti contenuti nel tipo selezionato nel riquadro superiore.

Screenshot of Referenced Objects report.

L'albero Tipi a cui si fa riferimento mostra i riferimenti mantenuti dal tipo selezionato nel riquadro superiore.

Screenshot of Referenced Objects report.

Per visualizzare le istanze di un tipo selezionato nel riquadro superiore, fare clic su "Visualizza istanze" accanto al tipo di oggetto.

Screenshot of the Instances view in the Memory Usage tool.

Screenshot of the Instances view in the Memory Usage tool.

La visualizzazione Istanze mostra le istanze dell'oggetto selezionato nello snapshot nel riquadro superiore. I riquadri Percorsi della radice e Oggetti a cui si fa riferimento mostrano gli oggetti che fanno riferimento all'istanza selezionata e i tipi a cui fa riferimento l'istanza selezionata. Quando il debugger viene interrotto nel punto in cui è stato preso lo snapshot, è possibile passare il mouse sulla cella Valore per visualizzare i valori dell'oggetto in una descrizione comando.

Report di tipo nativo

Scegliere il collegamento corrente di una cella Allocazioni (Diff) o Dimensioni heap (Diff) nella tabella di riepilogo Utilizzo memoria della finestra Strumenti di diagnostica.

Screenshot of Native Type View.

Screenshot of Native Type View.

La Visualizzazione Tipi mostra il numero e la dimensione dei tipi dello snapshot.

  • Scegliere l'icona delle istanze (The instance icon in the Object Type column) di un tipo selezionato per visualizzare informazioni sugli oggetti del tipo selezionato nello snapshot.

    La visualizzazione Istanze mostra ogni istanza del tipo selezionato. La selezione di un'istanza consente di visualizzare lo stack di chiamate che ha comportato la creazione dell'istanza nel riquadro Stack di chiamate allocazione .

    Screenshot of the Instances view and Allocation Call Stack pane.

  • Scegliere Visualizza istanze accanto a un tipo selezionato per visualizzare informazioni sugli oggetti del tipo selezionato nello snapshot.

    La visualizzazione Istanze mostra ogni istanza del tipo selezionato. La selezione di un'istanza consente di visualizzare lo stack di chiamate che ha comportato la creazione dell'istanza nel riquadro Stack di chiamate allocazione .

    Screenshot of the Instances view and Allocation Call Stack pane.

  • Scegliere Visualizzazione stack dall'elenco Modalità di visualizzazione per visualizzare lo stack di allocazione per il tipo selezionato.

    Screenshot of Stacks view.

  • Scegliere Stack per visualizzare lo stack di allocazione per il tipo selezionato.

    Screenshot of Stacks view.

Informazioni dettagliate sull'utilizzo della memoria

Per la memoria gestita, lo strumento analisi della memoria offre anche più potenti informazioni dettagliate predefinite. Selezionare la scheda Informazioni dettagliate nei report Tipi gestiti e mostra le informazioni dettagliate auto applicabili, ad esempio Stringhe duplicate, Matrici sparse e Perdite di gestori eventi.

Screenshot of the insight view in the Memory Usage tool.

La sezione Stringhe duplicate mostra l'elenco di stringhe che vengono allocate più volte nell'heap. Inoltre, questa sezione mostra la memoria totale sprecato, ovvero il (numero di istanze - 1) volte le dimensioni della stringa.

La sezione Matrici di tipo sparse mostra le matrici riempite principalmente con zero elementi, che possono risultare inefficienti in termini di prestazioni e utilizzo della memoria. Lo strumento di analisi della memoria rileverà automaticamente queste matrici e mostrerà la quantità di memoria sprecato a causa di questi valori zero.

La sezione Perdite di gestori eventi, disponibile in Visual Studio 2022 versione 17.9 Preview 1, mostra potenziali perdite di memoria che possono verificarsi quando un oggetto sottoscrive l'evento di un altro oggetto. Se il server di pubblicazione dell'evento scade nel sottoscrittore, il sottoscrittore rimane attivo, anche se non vi sono altri riferimenti. Ciò può causare perdite di memoria, in cui la memoria inutilizzata non viene liberata correttamente, causando l'uso di maggiore e maggiore memoria nell'applicazione nel corso del tempo.

Alcuni tipi sono noti per avere campi che possono essere letti per determinare le dimensioni della memoria nativa su cui si trovano. La scheda Informazioni dettagliate mostra i nodi di memoria nativi falsi nel grafico degli oggetti, che vengono mantenuti dai relativi oggetti padre in modo che l'interfaccia utente li riconosca e visualizzi le relative dimensioni e grafico di riferimento.

Screenshot of the native insight view in the Memory Usage tool.

Report di modifica (Diff)

  • Scegliere il collegamento di modifica in una cella della tabella di riepilogo della scheda Utilizzo memoria nella finestra Strumenti di diagnostica .

    Screenshot of Choose a change link in a cell.

    Screenshot of Choose a change link in a cell.

  • Scegliere uno snapshot dall'elenco Confronta con di un report gestito o nativo.

    Screenshot of Choose a snapshot from the Compare To list.

    Screenshot of Choose a snapshot from the Compare with list.

Il report di modifica aggiunge colonne (contrassegnate con (Diff)) al report di base che mostra la differenza tra il valore di snapshot di base e lo snapshot di confronto. Ecco un esempio di come potrebbe apparire un report delle differenze di visualizzazione del tipo nativo:

Screenshot of Native Types Diff View.

Screenshot of Native Types Diff View.

Blog e video

Analizzare CPU e memoria in fase di debug

Blog su Visual C++: profilatura della memoria in Visual C++ 2015

Passaggi successivi

In questa esercitazione si è appreso come raccogliere e analizzare i dati d'uso della memoria. Se è già stata completata la presentazione del profiler, è possibile leggere un approccio generale all'ottimizzazione del codice usando gli strumenti di profilatura.

In questa esercitazione si è appreso come raccogliere e analizzare i dati di utilizzo della memoria durante il debug. Per altre informazioni sull'analisi dell'utilizzo della memoria nelle build di versione, è possibile usare il Profiler prestazioni.