I was testing an application I am building with Xamarin/Android. One of the tests was rotating the device while Google Maps fragment being shown. By default each time the orientation changes the activity and its views/fragments are recreated. IOW it is a good test for possible memory leaks because the recreation can be frequent.
I was quite surprised seeing a huge memory increase each time. Those eventually lead to an OutOfMemory in Java stack somewhere in Google Maps. After double checking that managed Xamarin code wasn’t leaking (i.e. retaining the said fragment somehow) anything I was starting to wonder whether Google Maps fragment is leaking internally. Perhaps only when combined with Xamarin. That’d be odd but not impossible.
Thus I started Android Studio and crafted a repro in pure Java. It wasn’t leaking. Thus it must have something to do with Xamarin. But what? Xamarin is providing a component to Google Maps (.net wrapping). Is it possible that there is a problem? Switching back to Xamarin and a few more tests later I couldn’t create a non leaking version. No matter what I did I still got leaks.
Suspecting a problem within Xamarin somewhere I decided to report a bug to bugzilla (Xamarin’s bug reporting web, not support) about it and mail a question to Xamarin support. The Jonathan Pryor’s response on bugzilla was lightning fast. In an hour or so he solved the mystery. The response from official support line was fast as well. (kudos to Xamarin on both).
It turns out that I was bitten with unmanaged memory pressure issue. Actually with unmanaged managed memory pressure issue. The thing is that Xamain/.net doesn’t know anything about unmanaged memory consumption. This is well known to .net developers, or at least, it should be. So, if you allocate a huge unmanaged piece of memory the .net won’t know about its size and won’t fire garbage collection automatically when it is released even it you are running low on memory (there are ways to let it know the memory consumption but that isn’t automatic). That’s usually valid for native code. But in the case of Xamarin, Java part is also unmanaged code to Xamarin (albeit Java is managed, but managed within Java with its own garbage collector).
Each time the Google Maps fragment was created and destroyed Xamarin created and “released” a single reference to maps fragment. By released I mean it was referenced from anywhere anymore and thus available for garbage collection by Xamarin garbage collector. But since .net saw only few bytes in that reference (which in turn referenced a ton of Java objects) it didn’t fire garbage collection automatically (and Java garbage collector couldn’t reclaim the memory because it was referenced by Xamarin) thus memory usage was increasing by huge steps. Strictly speaking this wasn’t a memory leak because all the memory would be reclaimed eventually – when garbage collection would run.
And that’s exactly how I solved it – I now run a manual garbage collection at least upon fragment creation (to reclaim the memory used by previous one). Problem solved. The bottom line is that you might run manual garbage collection after releasing a reference to Java object which internally uses plenty of memory. Also, calling Dispose on Xamarin objects helps (they might release reference to Java’s counterpart). In my case I couldn’t call Dispose because my fragment was created by layout inflation (actually I guess, I could by obtaining a reference to it, but that wasn’t obvious nor it might not help).
Here is more about Xamarin Garbage Collection.