Analyze memory usage without debugging in the Performance Profiler (C#, Visual Basic, C++, F#)

The Memory Usage tool monitors your app's memory use. You can use the tool to study the real-time memory effects of scenarios you're actively developing in Visual Studio. You can take detailed snapshots of the app's memory states, and compare snapshots to find the root causes of memory issues. The Memory Usage tool is supported on .NET, ASP.NET, C++, or mixed mode (.NET and native) apps.

The Memory Usage tool can run with or without the debugger. In this article, we show how to use the Memory Usage tool without the debugger in the Visual Studio Performance Profiler, which is recommended for release builds. For information on choosing the best memory analysis tool for your needs, see Choose a memory analysis tool.

Memory Usage diagnostic sessions

To start a Memory Usage diagnostic session:

  1. Open a project in Visual Studio.

    The Memory Usage tool supports .NET, ASP.NET, C++, or mixed mode (.NET and native) apps.

  2. In the Debug menu, set the solution configuration to Release and select Local Windows Debugger (or Local Machine) as the deployment target.

  3. On the menu bar, select Debug > Performance Profiler.

  4. Under Available Tools, select Memory Usage, and then select Start.

    Start a Memory Usage diagnostic session.

Monitor memory use

When you start a diagnostic session, your app starts, and the Diagnostic Tools window displays a timeline graph of your app's memory use.

Screenshot of the Diagnostic Tools window in the Visual Studio Performance Profiler showing a timeline graph of the app's memory use.

The timeline graph shows memory fluctuations as the app runs. Spikes in the graph usually indicate that some code is collecting or creating data, and then discarding it when the processing is done. Large spikes indicate areas that you can optimize. Main concern is a rise in memory consumption that's not returned. This may indicate inefficient memory use or even a memory leak.

Take snapshots of app memory states

An app uses a large number of objects, and you might want to concentrate your analysis on one scenario. Or, you may find memory issues to investigate. You can take snapshots during a diagnostic session to capture memory usage at particular moments. It's good to get a baseline snapshot of an app before a memory issue appears. You can take another snapshot after the first occurrence of the problem, and additional snapshots if you can repeat the scenario.

To collect snapshots, select Take snapshot when you want to capture the memory data.

Screenshot of taking a snapshot.

Close the diagnostic session

To stop a monitoring session without creating a report, just close the diagnostic window. To generate a report when you're done collecting or have taken snapshots, select Stop Collection.

Screenshot of stopping the collection.

Screenshot of stopping the collection.

If you have trouble collecting or displaying data, see Troubleshoot profiling errors and fix issues.

Memory Usage reports

After you stop data collection, the Memory Usage tool stops the app and displays the Memory Usage overview page.

Screenshot of the overview page in the Memory Usage tool in the Visual Studio Performance Profiler, showing a memory usage graph and two snapshot panes.

Screenshot of the overview page in the Memory Usage tool in the Visual Studio Performance Profiler, showing a memory usage graph and two snapshot panes.

Memory Usage snapshots

The numbers in the Snapshot panes show the objects and bytes in memory when each snapshot was taken, and the difference between the snapshot and the previous one.

The numbers are links that open detailed Memory Usage report views in new Visual Studio windows. A snapshot details report shows the types and instances in one snapshot. A snapshot difference (diff) report compares the types and instances in two snapshots.

Screenshot of Snapshot view links

For C++, the Objects (Diff) column is named Allocations (Diff).

Image Description
Step 1 The total number of objects in memory when the snapshot was taken. Select this link to display a snapshot details report sorted by the count of instances of the types.
Step 2 The difference between the total number of memory objects in this snapshot and the previous snapshot. Select this link to display a snapshot diff report sorted by the difference in the total count of instances of the types.
Step 3 The total number of bytes in memory when the snapshot was taken. Select this link to display a snapshot details report sorted by the total size of the type instances.
Step 4 The difference between the total size of memory objects in this snapshot and the previous snapshot. A positive number means the memory size of this snapshot is larger than the previous one, and a negative number means the size is smaller. Baseline means a snapshot is the first in a diagnostic session. No Difference means the difference is zero. Select this link to display a snapshot diff report sorted by the difference in the total size of instances of the types.

Snapshot view links

Image Description
Step 1 The total number of bytes in memory when the snapshot was taken. Select this link to display a snapshot details report sorted by the total size of the type instances.
Step 2 The total number of objects in memory when the snapshot was taken. Select this link to display a snapshot details report sorted by the count of instances of the types.
Step 3 The difference between the total size of memory objects in this snapshot and the previous snapshot. A positive number means the memory size of this snapshot is larger than the previous one, and a negative number means the size is smaller. Baseline means a snapshot is the first in a diagnostic session. No Difference means the difference is zero. Select this link to display a snapshot diff report sorted by the difference in the total size of instances of the types.
Step 4 The difference between the total number of memory objects in this snapshot and the previous snapshot. Select this link to display a snapshot diff report. It’s sorted by the difference in the total count of instances of the types.

Managed types reports

Choose the current link of an Objects (Diff) cell in the Memory Usage summary table.

Screenshot of managed type report.

Note

For .NET code, the View Instances icon (The instance icon in the Object Type column) is only available while using the debugger-integrated Memory Usage tool or when you open a heap snapshot and choose Debug Managed Memory.

Screenshot of managed type report.

The top pane shows the count and size of the types in the snapshot, including the size of all objects that are referenced by the type (Inclusive Size).

The Paths to Root tree in the bottom pane displays the objects that reference the type selected in the upper pane. The .NET garbage collector cleans up the memory for an object only when the last type that references it has been released.

The Referenced Types tree displays the references that are held by the type selected in the upper pane.

Screenshot of Referenced Objects report.

The Referenced Types tree displays the references that are held by the type selected in the upper pane.

Screenshot of Referenced Objects report.

Report tree filters

Many types in apps aren't required to app developers. The snapshot report filters can hide most of these types in the Managed Memory and Paths to Root trees.

Sort and filter options

Sort and filter options

  • To filter a tree by type name, enter the name in the Filter box. The filter isn't case-sensitive, and it recognizes the specified string in any part of the type name.

  • Select Show Just My Code in the Filter dropdown to hide most instances that are generated by external code. External types belong to the operating system or framework components, or are generated by the compiler.

  • Select Collapse Small Objects in the Filter dropdown to hide types whose Size (Bytes) is less than 0.5 percent of the total memory.

Native types reports

Choose the current link of an Allocations (Diff) or Heap Size (Diff) cell in the Memory Usage summary table of the Diagnostic Tools window.

Screenshot of Native Type View.

Screenshot of Native Type View.

The Types View displays the number and size of the types in the snapshot.

  • Choose the View Instances icon next to a selected type to display information about the objects of the selected type in the snapshot.

    The Instances view displays each instance of the selected type. Selecting an instance displays the call stack that resulted in the creation of the instance in the Allocation Call Stack pane.

    Screenshot of the Instances view and Allocation Call Stack pane.

  • Choose the instances icon (The instance icon in the Object Type column) of a selected type to display information about the objects of the selected type in the snapshot.

    The Instances view displays each instance of the selected type. Selecting an instance displays the call stack that resulted in the creation of the instance in the Allocation Call Stack pane.

    Screenshot of the Instances view and Allocation Call Stack pane.

  • Choose Stacks to see the allocation stack for the selected type.

    Screenshot of Stacks view.

  • Choose Stacks View in the View Mode list to see the allocation stack for the selected type.

    Screenshot of Stacks view.

Memory Usage Insights

For managed memory, the Memory Analysis tool also gives multiple powerful built-in auto insights. Select the Insights tab in the Managed types reports and it shows the applicable auto insights like Duplicate strings, Sparse arrays, and Event handler leaks.

Screenshot of the insight view in the Memory Usage tool.

The Duplicate Strings section shows the list of strings that get allocated multiple times on the heap. In addition, this section shows the total wasted memory, that is, the (number of instances - 1) times the size of the string.

The Sparse Arrays section shows arrays that are mostly filled with zero elements, which can be inefficient in terms of performance and memory usage. The memory analysis tool will automatically detect these arrays and show you how much memory is being wasted due to these zero values.

The Event Handler Leaks section, available in Visual Studio 2022 version 17.9 Preview 1, shows potential memory leaks that can occur when one object subscribes to another object's event. If the publisher of the event outlives the subscriber, the subscriber remains alive, even if there are no other references to it. This can lead to memory leaks, where unused memory isn't properly freed, causing the application to use more and more memory over time.

Certain types are known to have fields that can be read to determine the size of the native memory they're holding onto. The Insights tab shows fake native memory nodes in the object graph, which are retained by their parent objects such that the UI will recognize them and display their size and reference graph.

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

Change (Diff) reports

  • Choose the change link in a cell of the Snapshot pane in the Memory Usage overview page.

    Screenshot of Choose a change link in a cell.

    Screenshot of Choose a change link in a cell.

  • Choose a snapshot in the Compare To list of a managed or native report.

    Screenshot of Choose a snapshot from the Compare with list.

    Screenshot of Choose a snapshot from the Compare To list.

The change report adds columns (marked with (Diff)) to the base report that show the difference between the base snapshot value and the comparison snapshot. Here's how a Native Type View diff report might look:

Screenshot of Native Types Diff View.

Screenshot of Native Types Diff View.

The top pane shows the count and size of the types in the snapshot, including the size of all objects that are referenced by the type (Inclusive Size).