Memory profiling is one of the pillars of a good application. Even more important is on mobile devices since memory is usually scarce there. But sadly, there is no memory profiler for Xamarin Android development. Actually there is some raw support but lacks UI and friendliness. Hence I’ve built my own memory profiler.
This is an alpha quality version.
How it works
Since version 4.14 Xamarin has a (not) very visible support for memory profiling. But even with this support it isn’t easy to profile. With each garbage collection a log file is written on the device containing all the relevant data in a text format. Note that once you turn profiling on with
adb shell setprop debug.mono.profile log:heapshot
This setting is global and it works on all Xamarin apps on the device. Remember to turn it off afterwards if you don’t need it anymore to avoid, I guess, a huge performance hit.
There are two problems with the existing support here:
- hard to trigger a garbage collection manually
- hard to analyze data
My solution tackles both problems. The solution is divided into two parts.
Server side
The library available through NuGet does two things:
- broadcasts its presence through UDP
- allows triggering garbage collection from client
The library supports API Level 4 (Xamarin v1.6) and above.
Here is the NuGet package URL. You can also install it using this Package Manager Console command:
Install-Package RhMemProfiler.Server –Pre
The package is marked as prerelease and it won’t appear in NuGet Package manager if it isn’t set for Include Prerelease.
Once package has been added go ahead and create a global variable of type RhMemProfiler.Server.Beacon and initialize it with a friendly name and package name, like:
RhMemProfiler.Server.Beacon beacon;
….
beacon.Start(“your friendly name”, PackageName);
PackageName is the name of your package and it is ContextWrapper’s property.
Client side
This is a Windows application distributed through ClickOnce. After installation you’ll be prompted to open the firewall for the application. This is required since it, well, uses UDP and TCP to communicate with the server side.
Private networks access is enough unless your mobile device is running on public network.
Once that is done you’ll be presented with a main window:
If your application on mobile device/emulator is running then you’ll see at least one option (one per IP on the device) in the combo box left of Connect button. That list contains friendly name (used when initializing on server) and its IP address. Pick any of them (IP doesn’t matter at this time). Hit Connect button.
If connection is successful you’ll see Conncted text in bottom left corner. If this is the first time you are connecting and/or memory profiling isn’t enabled yet on the device then you’ll be presented the Enable Profiling button, like:
Enable profiling, restart mobile device application, disconnect, connect again and your are good to start memory profiling:
Once there were at least two garbage collections and a snapshot has been taken afterwards you are able to compare them by selecting start and end version.
Collect Garbage … forces garbage collection on the mobile device
Snapshot … collects memory profiling log file from the device
Disable profiling … disables profiling on the device (profiling is per device, not per application – it affects all Xamarin apps on that device)
Only with source … shows only types from your package
Group panel … lets you group columns
Start and End … memory profile versions to compare to
Test
Imagine I have a class
public class MemLeak { public string Value; }
and a list of these somewhere defined. If I do a garbage collection before, when list is empty, and after one instance of MemLeak has been added I’ll see something like this when comparing before and after:
It is pretty much obvious that the count of MemLeak instances increased by 1 and there are total of two instances of it around (actually I created one before that’s why there are two of them).
Memory profiling strategy
Memory profiling works by comparing state before and after. Usually a garbage collection is taken before some action and after that action. If the number of some instance increased without a valid reason it means that the application is leaking memory. Professional memory profilers help you even pinpointing the source of the leaks, however in our case, Xamarin doesn’t have that data available through this strategy.
Known limitations
- ADB has to be in the path.
- Only one device or emulator can be connected to the computer at the same time (the combo lets you pick the application but not the IP)
Final words
This is an alpha release, so be gentle with it and do not complain if your computer explodes because of it. It is something I wanted to release even at this stage for you to play with. It is actually pretty functional. So, feedback is appreciated and so are questions but I don’t guarantee anything, the development is done in my spare time (like that there is any ) for now. Hope you’ll find it useful.
Hi Miha !
I've been overlooking your tool since you announced it, and gave a shot today.
I've got the client and server working, able to connect together, able to trigger a GC from the client (and see the "GC requested … done" on logcat), all that's good…
BUT 🙂
Grabbing a snapshot just does nothing :/
I've enabled profiling, I've checked the "/data/data/–packagename–/files/.__override__/profile.mlpd" does exists…
Also, I've checked, adb is in the PATH..
Is there any way of giving you more debug output ?
Cheers !
Hi Basile,
The name of the profile file is expected to be the same of the app. How is your app package named?
I tried your profiler but it doesn't work.
An IP appears in the list, I select it, then I press Connect. After a while Enable profiling button appears in the center of the window but when I press it, nothing happens.
Hi,
when I click on Enable profiling, nothing happens. Why is that?
Hi,
I did everything like you wrote but when I press connect, it showed "connected" left down but doesn't show "Enable profiling" button. Any idea?