7

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

Cœur
  • 37,241
  • 25
  • 195
  • 267
xleon
  • 6,201
  • 3
  • 36
  • 52
  • 3
    Relevant reading: https://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/#Cross-VM_Object_Collections and http://stackoverflow.com/questions/28863058/xamarin-android-finalizer-not-getting-called-when-leaving-the-activity-to-go-to/28868582#28868582 – matthewrdev Dec 26 '15 at 22:31

2 Answers2

10

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.

Christopher Schneider
  • 3,745
  • 2
  • 24
  • 38
-3

Calling the garbage collection doesn't ensure that the garbage collection will run or that all of the memory allocations will occur. Many times it is about removing a reference to an object. If a object has a reference to it, it will not be garbage collected. I've had to learn this the hard way. Basically to fix this issue at any point you can null out an object at any time and just set it when you need to. Example :

CustomObject cusObj = null;
if (cusObj == null) 
{
    cusObj = new CustomObject();
}

Simply making sure you null out the object removes the reference. But as stated in the answer above you can Garbage collect as well. Just remember the GC will not garbage collect an item with a reference. Take a look at :

Garbage collection and references C#

Community
  • 1
  • 1
yams
  • 942
  • 6
  • 27
  • 60
  • As you can read in the question, its not a reference problem cause I made sure to clean everything – xleon Dec 30 '15 at 17:11
  • Not true the biggest cause of heap space errors are reference to objects that wont allow the garbage collector to do there job. – yams Dec 30 '15 at 23:46
  • Nulling out your objects to get rid of the reference would have got rid of the problem in the first place. – yams Dec 30 '15 at 23:47
  • Again, if you read the question and the accepted answer carefully, you´ll realize in this case the problem comes from Mono not releasing Android Callable Wrappers. Doesn´t matter if you null every reference. The problem will persist until you call GC explicitly – xleon Dec 31 '15 at 10:06
  • Again if you read my statement nulling out objects so they can be garbage collected is what will get ensuring objects are garbage collected with out having to call the garbage collector. – yams Dec 31 '15 at 16:04
  • And a another excellent step for prevention is to set objects as IDisposableObject then dispose of objects when done. Calling the garbage collector doesn't guarantee when the GC will get called. If any questions see http://stackoverflow.com/a/3479281/5530425. Or you can reference here - https://msdn.microsoft.com/en-us/library/ms404247(v=vs.110).aspx. – yams Dec 31 '15 at 16:21
  • 1
    This question is domain specific to Xamarin.Android; the information is factually correct but xleons original question is very specific to the implementation details of monodroid. This answer doesn't consider the bridging mechanism monodroid implements to ensure Java and .NET objects lifecycles are always the same. – matthewrdev Dec 31 '15 at 23:04
  • I'm actually not looking to answer just share some information based on the title of the post "Shouldn´t GC run automatically in Xamarin.Android before runing out of memory". Even calling a GC.Collect doesn't ensure when it will run. Most people simply don't want to do necessary object deallocation and I'm really alarmed at how many people don't understand references and how to properly get rid of them. Ya know the idea that there are things that should be done but never are. – yams Jan 05 '16 at 16:25