Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shouldn´t GC run automatically in Xamarin.Android before running out of memory?

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:

enter image description here

I saw the memory going up constantly in Xamarin Profiler every time the activity opened:

enter image description here

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:

enter image description here

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

like image 495
xleon Avatar asked Dec 26 '15 19:12

xleon


1 Answers

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.

like image 180
Christopher Schneider Avatar answered Nov 15 '22 17:11

Christopher Schneider