I spent almost a whole day trying to find out the reason of a memory leak in Android. There´s an activity that I open/close many many times (with a timer). After a while I was getting OutOfMemory errors:
I saw the memory going up constantly in Xamarin Profiler every time the activity opened:
I made sure there were no properties or event handlers that could be stuck in that activity. I even removed every image, buttons, etc, trying to detect what was causing the memory leak. Still the same...
Then I did GC.Collect()
in the OnResume
method of main activity (the one that opens the problematic activity). Now I can see the memory going up and down as it should. You can see the result in the screenshot:
As per Xamarin docs:
The GC will run when the minor heap has run out of memory for new allocations
But that is not actually happening
You may want to read a bit further down in your link under Helping the GC:
The GC has an incomplete view of the process and may not run when memory is low because the GC doesn't know that memory is low.
and
Managed Callable Wrappers do not add additional instance members
Basically, it would appear BaseActivity is an Android Callable Wrapper (ACW) and the Mono GC doesn't know how big it is, so it doesn't know to invoke the garbage collector. From what I gather, the ACW is a way to implement Android interfaces.
The solution is, as you discovered, to manually call the garbage collector which is recommended in the Xamarin docs when using ACW's.
http://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/
----
Since the original posting of this answer, the Xamarin docs have improved in their explanation of this situation:
An instance of a Java.Lang.Object type or derived type is at least 20 bytes in size. Managed Callable Wrappers do not add additional instance members, so when you have a Android.Graphics.Bitmap instance that refers to a 10MB blob of memory, Xamarin.Android's GC won't know that – the GC will see a 20-byte object and will be unable to determine that it's linked to Android runtime-allocated objects that's keeping 10MB of memory alive.
This indicates that, regardless of whether objects are set to null or not, you should still manually call the Xamarin GC if you're allocating/deallocating Callable Wrappers that can potentially consume a large amount of memory.
Operating under the assumption a Java Object
is 20 bytes, if one were to allocate and null 100 objects that consume 10MB each, the Xamarin GC believes that 4000 bytes of memory are in use. In reality, ~1GB is in use, and the GC may or may not be invoked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With