Shouldn´t GC run automatically in Xamarin.Android

2019-02-05 03:42发布

问题:

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

回答1:

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.



回答2:

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#