none
Still unclear on how to properly dispose UserControls

    Question

  • I ran across a few older threads which discussed garbage collection of UserControls after .Clear() is called on their parent container:

    http://silverlight.net/forums/p/33473/102832.aspx
    http://silverlight.net/forums/p/5986/18356.aspx

    The rule seems to be that if you call Children.Clear() on a UserControl's parent, and no code holds a reference to that control, it's eligible for garbage collection. So I threw together a quick project to test this (solution posted at:  http://www.darrenmart.com/disposetester.zip  )

    Here's the gist of the test:

    - UserControl called ZoneInteractiveAreaTest, which derives from Canvas. I added an instance of this control to Page.xaml, and inside that instance is one Path. A snippet of the XAML:

    <Grid x:Name="LayoutRoot" Background="#FF363636">
       <DisposeTester:ZoneInteractiveAreaTest x:Name="ziaTest">
         
    <Path Height="149.424" Width="254.57" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000" Data="M22,35 C29,..."/>
      </DisposeTester:ZoneInteractiveAreaTest>
    </Grid>

    In the code I call LayoutRoot.Children.Clear, then GC.Collect().  The destructor is never called for ziaTest.  However, if I explicitly set ziaTest = null after Children.Clear, then the destructor is called and the memory freed. I know that any lingering event handlers or object references would keep ziaTest alive, but I haven't wired any events. What exactly is holding a reference to ziaTest in this case?

    I even tried implementing IDisposable on the custom control. In Dispose() I tried cycling through the children (in this case, the one Path) and setting them to null:

    for (int x = this.Children.Count - 1; x >= 0; x--)
    {
      
    UIElement elem = this.Children[x] as UIElement;
       elem =
    null;
    }

    .... but no dice. I think I'm missing the big picture here. It seems wrong that I'd need to explicitly set all the UserControls to null to free up the memory.

    Wednesday, March 11, 2009 4:36 AM

Answers

  • The issue you have is because you gave your DisposeTester an  X:Name.

     If you had not given it an X:Name it would probably be disposed.

    Here is how it happens:

    When your XAML is compiled, some code is generated in a file with the name of your xaml file (without .xaml) appending to it ".g.cs" (or close to that).

    In there code is gererated to set the class property that you can use in your class code:

    As an example (this is not 100% like this but just to explain how it goes)

     

    public partial class class name>
    {
      public DisposeTester ziaTest { get; set; }
    
      protected SomeInitMethod()
      {
         LoadTemplate(.....)
         ziaTest = FindControl(....)
      }
    
      // ... other generated code
    }
    
     

    So, this actually counts as a reference to you object.

    Hope this clarifies it.

    Thursday, March 12, 2009 11:07 AM

All replies

  • Thanks for bringing this up!

    I'm curious to know this too. You know, I come from c++ and I liken not explicitly freeing up memory to walking outside without any clothes on. Just feels weird.

    Currently, I'm just calling Clear on the parent container and setting App.Current.AutoMagical to true -- and I'm hoping everything automagically takes care of itself. I had been assuming that it was working. Apparently, not so much.

    Wednesday, March 11, 2009 7:41 AM
  • Hi. I'm fairly new to silverlight, but I might be able to help a little, but I don't have the experience with it yet to explain it as well as I want, so bear with me. 
    I think that if you were to create a second usercontrol and you changed this.content between the current usercontrol and the new one that you would find the original one would deconstruct. I've actually messed around with something quite similar just today and found that the current usercontrol (the one silverlight is currently working with) is referenced by your current content, as its contained within it.

    I might not be explaining it very well and I don't have access to .NET where I am currently so I can't write an example. But if you try messing around with doing something similar with how Jesse Liberty does with his multiple page example videos that you'll find the same thing I did.
    I think in your example that the reason its not deconstructing until you set it to null is because its the only usercontrol the silverlight app has to work with. If you had multiple usercontrols it would react differently. Hope this helps.

    Thursday, March 12, 2009 5:30 AM
  • The issue you have is because you gave your DisposeTester an  X:Name.

     If you had not given it an X:Name it would probably be disposed.

    Here is how it happens:

    When your XAML is compiled, some code is generated in a file with the name of your xaml file (without .xaml) appending to it ".g.cs" (or close to that).

    In there code is gererated to set the class property that you can use in your class code:

    As an example (this is not 100% like this but just to explain how it goes)

     

    public partial class class name>
    {
      public DisposeTester ziaTest { get; set; }
    
      protected SomeInitMethod()
      {
         LoadTemplate(.....)
         ziaTest = FindControl(....)
      }
    
      // ... other generated code
    }
    
     

    So, this actually counts as a reference to you object.

    Hope this clarifies it.

    Thursday, March 12, 2009 11:07 AM
  • MComeau,

    You were right... I stripped off the x:Name and it was disposed. Any way to tell the CLR, "I don't care if I named it, dispose it anyway"?  What you said about the .g.cs reference makes some sense, but then it also creates a quandary.

    When I'm designing in Blend it's conceivable that I'll have dozens of custom controls. Obviously it's very helpful to have intuitive names in the tree instead of twenty generic [MyUserControl] tags. Now that I know about this x:Name/disposal issue, it seems I'll have to wait until I'm finished designing in Blend, then strip off all the names so that the control is fully disposed in the application.

    Seems odd?

     

    Thursday, March 12, 2009 2:12 PM
  • Well, see it this way,

    You probably want to expose only controls you will want to be able to access from code. I agree that having decorative names would be a nice plus but its not there and probably wont be. Althought i dont believe it would be that hard for Expression Blend team to create some ignorable attributes that they could use for their designer.

    But at least, you have a fresh perspective on what caused your problem. You should be able to move forward, geared with that new knowledge. Smile

    Thursday, March 12, 2009 2:32 PM
  • it seems I'll have to wait until I'm finished designing in Blend, then strip off all the names so that the control is fully disposed in the application.

    Lame.

    Oh well, thanks for explaining the answer, it's good to know.
    Thursday, March 12, 2009 2:48 PM
  • Seems like you guys have solved this, but for future reference take a look at this post on Delay's blog on how to detect memory leaks and prevent them.

    Saturday, March 14, 2009 11:57 AM
  • Thanks for the link bryant, that's a very good blog post. I'm new to the WinDbg/SoS scene and finding it a little overwhelming so far (I didn't even know about !DumpObj, no wonder I hit so many dead-ends)... but that post clears up some of my confusion.

    Cheers,
    Darren

     

    Saturday, March 14, 2009 5:35 PM