Identifying Memory Leaks with dotnet-dump and dotnet-gcdump
In this tutorial, you will learn how to monitor memory usage of a Tizen .NET application and identify possible memory leaks using dotnet-dump and dotnet-gcdump.
Note: The total memory usage of an application process is affected by various factors (for example, shared size, swap size, or memory allocated by the runtime for its internal use). Only the managed memory region (GC heap) can be analyzed with dotnet-dump and dotnet-gcdump.
You need the following things installed on your device:
- .NET diagnostic tools (see Installing .NET diagnostic tools on Tizen devices)
- Target application
Monitoring memory usage
First, obtain the process ID of the currently running application process. Use
dotnet dump ps to list all .NET processes running on the device.
sh-3.2$ dotnet counters ps 17876 dotnet-loader /usr/bin/dotnet-loader 17464 LeakMemory.dll /usr/bin/dotnet-loader
To examine the current memory usage of the target process, use
dotnet counters monitor.
sh-3.2$ dotnet counters monitor -p 17464 Press p to pause, r to resume, q to quit. Status: Running [System.Runtime] % Time in GC since last GC (%) 0 Allocation Rate / 1 sec (B) 56,992 CPU Usage (%) 29 Exception Count / 1 sec 0 GC Heap Size (MB) 34 Gen 0 GC Count / 60 sec 0 Gen 0 Size (B) 12 Gen 1 GC Count / 60 sec 0 Gen 1 Size (B) 13,820 Gen 2 GC Count / 60 sec 0 Gen 2 Size (B) 493,068 LOH Size (B) 33,587,792 Monitor Lock Contention Count / 1 sec 0 Number of Active Timers 3 Number of Assemblies Loaded 52 ThreadPool Completed Work Item Count / 1 sec 61 ThreadPool Queue Length 0 ThreadPool Thread Count 2 Working Set (MB) 89
In this tutorial, we are only interested with the managed heap size (
GC Heap Size) of the process. (cf.
Working Set indicates the total bytes of memory used by the process.) If the heap size unexpectedly grows over time, it is likely that your application has a memory leak.
Analyzing managed heap dump with dotnet-dump
To capture a memory dump (coredump) from the target process, use
dotnet dump collect.
sh-3.2$ dotnet dump collect -p 17464 -o /home/owner/share/coredump.17464 Writing full to /home/owner/share/coredump.17464 Complete
Make sure the target process has write access to the specified path. Also ensure that there's enough space available on the disk (
df -h /opt/usr) because the size of the dump is often very large (200+ MB). Otherwise, you will see a file write error (
To analyze the generated coredump, use
dotnet dump analyze.
sh-3.2$ dotnet dump analyze /home/owner/share/coredump.17464 Loading core dump: /home/owner/share/coredump.17464 ... Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command. Type 'quit' or 'exit' to exit the session. >
You can now type any SOS command in the command prompt. For example,
clrstack -all provides stack traces of all managed threads.
If we want to find managed objects within the GC heap that caused a memory leak, the
dumpheap -stat command provides the overall statistics about the managed heap.
> dumpheap -stat Statistics: MT Count TotalSize Class Name ... f026e634 60 35556 System.Object f026f3b8 30 46038 System.Char f026fc3c 245 51356 System.Int32 f0290744 1531 133922 System.String e3706968 2 16777240 Xamarin.Forms.ContentPage
The last line reveals that the memory leak is caused by
Xamarin.Forms.ContentPage type objects. Use the
gcroot command to print the chain of references that are keeping those objects alive.
> dumpheap -type Xamarin.Forms.ContentPage Address MT Size f126391c e38cad40 12 d9c82010 e38cad40 33554444 Statistics: MT Count TotalSize Class Name e38cad40 2 33554456 Xamarin.Forms.ContentPage Total 2 objects > gcroot d9c82010 Thread 4438: FF8BF398 F7023CD0 <unknown> ... -> F12637B0 LeakMemory.Views.CircularButtonPage -> F1263908 System.Collections.Generic.List`1[[Xamarin.Forms.ContentPage, Xamarin.Forms.Core]] -> D9C82010 Xamarin.Forms.ContentPage Found 1 unique roots (run 'gcroot -all' to see all roots).
As shown, the leaky objects are held by a
LeakMemory.Views.CircularButtonPage type object.
This is the general procedure of identifying major sources of memory leaks in your application. You can continue investigating the coredump using various SOS commands and options to find other underlying issues in your code.
Analyzing managed heap dump with dotnet-gcdump
An alternative to dotnet-dump is dotnet-gcdump which also allows you to track object allocation and memory leaks from a running application process. GC dumps are often easier to use than coredumps because they are portable (cross-platform format) and lightweight (very small size). Find more information about GC dumps at Collecting and analyzing memory dumps.
To capture a GC dump (snapshot) from the target process, use
dotnet gcdump collect. You can take multiple snapshots from the same process while it's running.
sh-3.2$ dotnet gcdump collect -p 17464 -o /home/owner/share/17464.gcdump Writing gcdump to '/home/owner/share/17464.gcdump'... Finished writing 11101371 bytes.
Using Visual Studio
You can use Visual Studio to open and analyze the generated GC snapshot. Copy the
.gcdump file from the device to the host by typing
sdb pull /home/owner/share/17464.gcdump and open it in Visual Studio.
By sorting the objects by their types and sizes,
List<Xamarin.Forms.ContentPage> appears at the top of the table, which is the main source of the memory leak. The chain of references is also shown in the bottom panel (Paths to Root).
If you have more than one snapshot, compare two different snapshots using diff to see which objects grow fast over time. Choose a snapshot that you want to serve as the baseline in the
Compare to: section.
If the information obtained above is not enough for you, you can switch to another tool called PerfView. At first glance, this tool doesn't look very intuitive, but is extremely helpful for performing complex analysis of managed heap data.
Tip: PerfView also supports the trace file format (
.nettrace) used by the dotnet-trace tool (see Finding Performance Bottlenecks with dotnet-trace). You can specify detailed provider keywords to capture certain event information (for example, JIT or GC) when recording trace data.
By switching between a set of different views and filtering necessary data, you can get detailed information about managed objects in the GC heap. Pressing F1 anywhere opens a pop-up window with the help page for the current view. Also read the following pages for further details of the PerfView tool. Note that some parts of the documents are outdated or not generally applicable for Tizen (PerfView had been Windows-only for a long time before .NET Core was released in 2016).