-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Memory Leaks
The purpose of this doc, is to be a guide on:
- Tooling to identify & diagnose leaks
- Understanding C# code patterns that cause memory leaks
- Techniques to fix leaks
We've tried to tag various PRs and Issues related to memory issues at:
The best way to understand memory usage is to take a "snapshot" of all C#
objects in memory at a given time. The dotnet-gcdump tool is one way
you can do this for programs running on the CoreCLR runtime.
You can also use Visual Studio while debugging via Debug > Windows >
Diagnostic Tools. On the Memory Usage tab, you can take snapshots

After taking a snapshot, you can open it to view every managed (C#) object in memory and inspect what objects hold references in a tree view:

This same view is available in Visual Studio when opening *.gcdump files
created by dotnet-gcdump or other tooling. *.gcdump files can also
be opened in PerfView, but as of writing there is no way to open
these files on non-Windows platforms.
Note Although taking memory snaphots while debugging can be quite convenient, note that you need to disable XAML hot reload for them to be accurate. In recent versions of Visual Studio, the
Managed Memorywindow will display a warning if you forget this step.
Note You might also consider taking memory snapshots of
Releasebuilds, as code paths can be significantly different in configurations where XAML compilation, AOT compilation, and trimming are enabled.
Unfortunately, .NET MAUI apps running on Android, iOS, and macOS are running on
the Mono runtime, and so the same support isn't there quite yet.
dotnet-gcdump support for the Mono runtime is coming in a future
preview of .NET 8:
We are hopeful this will become much easier in the future. For now, we still
have a way to record *.gcdump files from the Mono runtime -- just in a much
less convenient manner.
To illustrate the process, let's look at how you can record *.gcdump files
from a .NET MAUI application running on macOS via Catalyst.
- Launch the app with
$DOTNET_DiagnosticPortsset:
$ DOTNET_DiagnosticPorts=~/my-dev-port,suspend ./bin/Debug/net7.0-maccatalyst/maccatalyst-x64/MyApp.app/Contents/MacOS/MyAppIn the output directory, macOS applications can be found in the *.app folder
(bundle). We can run the main binary directly with the $DOTNET_DiagnosticPorts
environment variable set in our terminal. The app will pause at this point,
waiting for an instance of dotnet-trace to connect to it.
- Launch
dotnet-tracewith a special "provider" in a different terminal window:
$ dotnet-trace collect --diagnostic-port ~/my-dev-port --providers Microsoft-DotNETRuntimeMonoProfiler:0xC900001:4This allows the app to fully launch, while dotnet-trace saves a *.nettrace
file with object allocation from the Mono runtime information inside.
When trying to record a precise snapshot, I normally Ctrl+C dotnet-trace to
cancel the first recording. Then I navigate to the appropriate place in the app
where the problem occurs and start a fresh recording. Each time dotnet-trace
connects to the running app, it will create a fresh *.nettrace file with
current snapshot information.
- Convert the
*.nettracefile to a*.gcdumpfile we can open in Visual Studio.
There is a tool from Filip Navara we can use for this: mono-gcdump.
Clone the source code for this tool locally and build & run it:
dotnet run -- convert my-dev-port.nettraceThis should output a my-dev-port.gcdump file in the same directory the
*.nettrace file was located.
To attach dotnet-trace to a running app on iOS & Android, the
dotnet-dsrouter tool is used to See further information about
using dotnet-trace and dotnet-dsrouter together for specific
platforms at:
TBD
TBD