When does memory associated with custom renderers get garbage collected?

JohnHardmanJohnHardman GBUniversity mod

On UWP, I see my custom renderer instances being finalized. However, on Android and iOS, the finalizers are not being called. Has anybody else encountered this? If so, is there a solution?

(I am still using XF 2.4.x - I'll be upgrading at some point, but am trying to avoid the pain involved a while longer if I can).

Answers

  • robbitrobbit CNMember Xamurai

    Hi @JohnHardman

    Xamarin.Android is just a wrapper for android, you can read this, there is ACW, c# code will generate ACW called by Android platform's JVM.

    On Android, there is finalize() method, you can read this:

    it's best not to rely on finalize() to do any cleaning up etc.

    So, about Object-Oriented Programming(Java and C#),the JVM or CLR will help us to destroy the object which has been abandoned.

  • JohnHardmanJohnHardman GBUniversity mod
    edited July 2018

    @robbit - It looks like something is holding a reference to the custom renderers on Android and iOS that prevents the custom renderers' C# finalizers being called, whereas on UWP there isn't a reference being held. It's not my code that's holding references to the renderers. Is there something in XF that would be doing this? If so, how can I make XF release the renderers so that the garbage collector can get rid of them?

    Is it possible that exporting the renderer results in an instance of the renderer being held by XF?

    [assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
    
  • ElvisXiaElvisXia Member, Forum Administrator, Xamarin Team Xamurai

    @JohnHardman it's not the finalizers that get called, but the Dispose method like below:

    public class MyEntryRenderer:EntryRenderer
    {
        public MyEntryRenderer(Context context) : base(context)
        { }
    
        ~MyEntryRenderer()
        {
             //This won't get called
        }
    
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            //this will get called
        }
    }
    

    Is there something in XF that would be doing this? If so, how can I make XF release the renderers so that the garbage collector can get rid of them?

    If you test it through overlapping a new page, MyEntryRenderer will never be collected, because it's still in the pages stack. You need to popup the page, then your renderer will be collected.

  • JohnHardmanJohnHardman GBUniversity mod
    edited July 2018

    @ElvisXia said:
    If you test it through overlapping a new page, MyEntryRenderer will never be collected, because it's still in the pages stack. You need to popup the page, then your renderer will be collected.

    This is what I would expect to happen. This is what happens on UWP. However, on Android and iOS the renderers are not being collected. I am still using XF 2.3.4 - do you know if a fix has been implemented since 2.3.4 to resolve memory leaks related to renderers not being collected? If it is confirmed that a fix has been implemented, that might be the trigger for me upgrading (I am expecting the upgrade to 3.0 to be painful, so am avoiding it unless necessary).

  • ElvisXiaElvisXia Member, Forum Administrator, Xamarin Team Xamurai

    @JohnHardman I do not have XF 2.3.4 environment. So I just tested it on XF 3.0.0.561731, and it just get collected correctly. So maybe it is an issue of XF 2.3.4.

  • JohnHardmanJohnHardman GBUniversity mod
    edited May 2019

    @ElvisXia said:
    @JohnHardman it's not the finalizers that get called, but the Dispose method like below:

    public class MyEntryRenderer:EntryRenderer
    {
        public MyEntryRenderer(Context context) : base(context)
        { }
    
        ~MyEntryRenderer()
        {
             //This won't get called
        }
    
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            //this will get called
        }
    }
    

    Apologies for the delay. Just re-visiting this. As you say, Dispose is being called, but the finalizer rarely does (it does very occasionally on UWP (where I do most of my initial testing), but even then it might just be for one instance of the custom renderer, but not the other however many dozens or hundreds - seems strangely inconsistent, unless there is a race condition or some inconsistent coding at work somewhere).

    Where you say "This won't get called" for the finalizer, does that mean that a running app slowly accumulates custom renderer instances, for which Dispose has been called (to release unmanaged resources), but where the managed C# object has not been finalized / garbage collected?

    Looking at the Microsoft document for Dispose, I see https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose#implementing-the-dispose-pattern-for-a-derived-class

    That documentation says:
    "to implement the dispose pattern for a derived class, you provide the following:" ...
    "Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. The SafeHandle class provides a finalizer that frees you from having to code one. If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false."

    I had been doing the latter of those two options. Which of the approaches that the Microsoft documentation says should be done is the one recommended for custom renderers in Xamarin.Forms? Is there an example of the recommended approach anywhere, written in such a way that there are no leaks?

    cc. @ClintStLaurent and @NMackay - interested in your thoughts, in case I am missing something...
    cc. @noob8129 - as you asked about renderer lifecycles some time back
    cc. @robbit

  • JohnHardmanJohnHardman GBUniversity mod

    @jezh @LandLu @ColeX @yelinzh @JGoldberger - Could somebody have a look at the post above please ( https://forums.xamarin.com/discussion/comment/374579/#Comment_374579 ) and let me know whether the C# managed object that is a custom renderer ever gets garbage collected in XF. I know that Dispose gets called, but that's for releasing unmanaged resources associated with the renderer. Does the renderer itself ever get collected?

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    @JohnHardman

    There are some things that might cause the renderer to not be collected, for instance it your renderer subscribes to any events and you do not unsubscribe to them, that c an cause a memory leak, but that is something that is in .NET as well and is expected behavior. If the object that is publishing the event is still alive, it will keep ay objects subscribing to the event alive so that when it raises the event, the objects that subscribe to the event are still alive. See:

    https://michaelscodingspot.com/5-techniques-to-avoid-memory-leaks-by-events-in-c-net-you-should-know/
    https://blogs.msdn.microsoft.com/tess/2006/01/23/net-memory-leak-case-study-the-event-handlers-that-made-the-memory-baloon/

    Also when the garbage collector runs depends on memory pressure, and the custom renderer might not get collected right away when the view / page closes, even if there really are no more references to the custom renderer object. You can see if manually running GC.Collect() collects the object. If it does, there is no leak, and it was only that the GC did not run yet to collect the object. More info on the GC and when it runs:

    https://docs.microsoft.com/en-us/xamarin/android/internals/garbage-collection

    Of course, though, it is possible that there is a bug in Xamarin.Forms. I know you have some reason to stay on that old version (2.4.x), but there have been a number of issues fixed since then, including some memory leak issues so I highly recommend updating and dealing with whatever issue is holding you back from updating to the latest stable version of XF.

  • JohnHardmanJohnHardman GBUniversity mod

    @JGoldberger said:
    Of course, though, it is possible that there is a bug in Xamarin.Forms. I know you have some reason to stay on that old version (2.4.x), but there have been a number of issues fixed since then, including some memory leak issues so I highly recommend updating and dealing with whatever issue is holding you back from updating to the latest stable version of XF.

    I am using 3.4.0.1008975 currently. I'll be updating again fairly soon, but the code snippet from @ElvisXia included:

             ~MyEntryRenderer()
             {
                  //This won't get called
             }
    
             protected override void Dispose(bool disposing)
             {
                 base.Dispose(disposing);
                 //this will get called
             }
    

    which makes it sound intentional (or at least known) that a renderer's finalizer does not get called.

    I have checked this even with bare-bones renderers (so not subscriptions, event handlers, lambdas etc), and am explicitly triggering garbage collections. Every other type of object has its finalizer called, but not custom renderers (other than a very rare occasion when one out of the hundreds of instances gets called - that's very odd). I am testing using the UWP build at the moment, but have seen the same lack of renderer finalizers being called on Android & iOS when I have tested on those in the past.

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    @JohnHardman

    To be honest, I am not expert in Finalizers and IDisposable, but as far as I know you really should not have to deal with that yourself, and I have been told that it is definitely NOT recommended to make your own finalizers as they are difficult to do correctly.

    Why are you choosing to override the Finalizer rather than the Dispose method? It seems that would be the better approach. Looking over the Forms renderer source code, I do not see finalizers implemented in any of the renderers or renderer base classes. Also it seems all of the Android renderers ultimately inherit from XamarinFormsViewGroup, which is actually a bound java class (you can see that is a Xam.Android binding project) which would not have a .NET finalizer, so the ultimate base class for the renderers is not a C#.NET object, and that can be why there is no finalizer called. But you should not need a finalizer to be called to release unmanaged resources that you may have used in your custom renderer. You should just have to call Dispose() on, and null out, any references to C# wrappers around unmanaged resources and this could be done in the renderers Dispose method. But again, I am not the expert.

    If you feel there is a memory leak and you can provide a test project that demonstrates this, your best path forward is to file an issue on the Xamarin.Forms repository](https://github.com/xamarin/Xamarin.Forms/issues "file an issue on the Xamarin.Forms repository") with test project and steps to reproduce.

  • JohnHardmanJohnHardman GBUniversity mod
    edited May 2019

    @JGoldberger said:
    @JohnHardman

    To be honest, I am not expert in Finalizers and IDisposable, but as far as I know you really should not have to deal with that yourself, and I have been told that it is definitely NOT recommended to make your own finalizers as they are difficult to do correctly.

    Why are you choosing to override the Finalizer rather than the Dispose method? It seems that would be the better approach.

    I have conditionally-compiled finalizers, that when the related symbol is defined do nothing more than check for memory leaks. It's a simple pattern for those of us who don't have access to the Xamarin Profiler. I don't do any clean-up in the finalizers - it's purely a way of checking for managed memory leaks. I use Dispose for cleaning up any unmanaged resources.

    If you feel there is a memory leak and you can provide a test project that demonstrates this

    I suspect that there is a memory leak, and probably has been since day #1 of XF. The reason I asked about it in the forum is that I would have expected somebody else to have spotted it long ago if that is the case. Or, alternatively, that I am missing something - it seems unlikely that XF would resurrect disposed objects, but it's possible. Again, in that case, I would hope that somebody at Xamarin would have read this thread and immediately gone "yes, we do clever stuff behind the scenes to optimise memory management around renderers". That's why I was interested in the post from @ElvisXia, where his code comment explicitly said that the finalizer will not be called. It's as if he knows something that the rest of us should know - after all, if every XF app is slowly leaking resources when renderers are done with, any app that involves lots of navigating between pages (where the pages are newed and deleted rather than being re-used) will eventually terminate with an out-of-memory exception. I'm hoping I am wrong, but the question needs asking. @ElvisXia has been active on the forum within the last 24 hours, so I would hope that he could give a definitive answer.

    In terms of posting a project, any existing project that uses a custom renderer will do. Just add a finalizer to the renderer and put a breakpoint in it. Check it on UWP first, before doing Android and iOS.

  • ElvisXiaElvisXia Member, Forum Administrator, Xamarin Team Xamurai
    edited May 2019

    @JohnHardman Sorry for late response, been busy recently.

    Where you say "This won't get called" for the finalizer, does that mean that a running app slowly accumulates custom renderer instances, for which Dispose has been called (to release unmanaged resources), but where the managed C# object has not been finalized / garbage collected?

    That's not what I meant. What I meant is that finalizer won't be called at the time point when dispose get called. I didn't do a deep research when the finalizer will get called, because generally it is controlled by GC. @JGoldberger is correct, if you want to see if there is memory leak in project, you can simply manually call GC.Collect().

    Every other type of object has its finalizer called, but not custom renderers (other than a very rare occasion when one out of the hundreds of instances gets called - that's very odd). I am testing using the UWP build at the moment, but have seen the same lack of renderer finalizers being called on Android & iOS when I have tested on those in the past.

    I didn't go through the source codes to see how customer renderer is implemented. So I can't tell you what is happening down there. But you are welcomed to raise this issue on the Xamarin.Forms Github Repo just like JGoldberger said.

  • JohnHardmanJohnHardman GBUniversity mod

    @ElvisXia said:
    if you want to see if there is memory leak in project, you can simply manually call GC.Collect().

    As I said above, I am explicitly calling GC.Collect() and renderers are still not being finalized.

    @ElvisXia said:
    I didn't go through the source codes to see how customer renderer is implemented. So I can't tell you what is happening down there. But you are welcomed to raise this issue on the Xamarin.Forms Github Repo just like JGoldberger said.

    I was hoping one of the support team would do a quick double-check before I log it. All it needs is to add a finalizer to an existing renderer and then add a breakpoint (ok, explicitly call GC.Collect if in a hurry)...

  • JohnHardmanJohnHardman GBUniversity mod

    @JohnHardman said:
    I was hoping one of the support team would do a quick double-check before I log it. All it needs is to add a finalizer to an existing renderer and then add a breakpoint (ok, explicitly call GC.Collect if in a hurry)...

    In the absence of a double-check by support that would have taken all of 5 minutes, I've logged the issue. I understand that it's more interesting to answer questions where coding is required rather than checking a breakpoint gets called, but really...

  • JohnHardmanJohnHardman GBUniversity mod
    edited May 2019

    It appears that VisualElementRenderer erroneously (IMHO) calls GC.SuppressFinalize. Hopefully, the defect report will result in that being removed, so that it is up to the developer of custom renderers to decide whether or not to use it.

    In the meantime, I have put GC.ReRegisterForFinalize(this); in my conditionally-compiled code, which now makes finalizers get called. For those of us without access to the Xamarin profiler, this gives us a way of checking for leaks, implementing a consistent pattern for all classes that we want to track, not just non-renderer classes.

Sign In or Register to comment.