views:

669

answers:

4

Update: I tried this on another, more cleanly installed, machine. I could not reproduce this on that machine. If I find out what offending (VSStudio) component causes this, I will let you know.

I create some UIElements from code behind and was anticipating the garbage collection to clear up stuff. However, the objects are not free-ed at the time I expected it. I was expecting them to be freeed at RemoveAt(0), but they are only freed at the end of the program.

How can I make the objects be freed when removed from the Children collection of the Canvas?

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
    MouseDown="Window_MouseDown">
  <Grid>
    <Canvas x:Name="main" />
  </Grid>
</Window>

The code behind is:

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
  }

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
  GC.Collect(); // This should pick up the control removed at a previous MouseDown
  GC.WaitForPendingFinalizers(); // Doesn't help either

  if (main.Children.Count == 0)
    main.Children.Add(new MyControl() { Background = Brushes.Yellow, Width = 100, Height = 50 });
  else
    main.Children.RemoveAt(0);
 }
}

public class MyControl : UserControl
{
  ~MyControl()
  {
    Debug.WriteLine("Goodbye");
  }
}
A: 

Objects in C# are not automatically "freed" as soon as they are no longer used.

Rather, when you remove the object from your Control, it becomes eligible for garbage collection at that point, assuming you have no other references to that UIelement.

Once an object is "unrooted" (there are no references, directly or indirectly, from any used object in your application), it becomes eligible for collection. The garbage collector will then, eventually, clean up your object, but when this happens is not something you (typically) control.

Just trust that it will eventually get cleaned up. This is the beauty of C# (and .NET in general) - the management, and worry, of this is handled for you.


Edit: After some testing, it appears that the Window holds a reference to the UIelement until the next layout pass. You can force this to occur by adding a call to:

this.UpdateLayout();

After removing the element(s) from the canvas Children. This will cause the objects to be available for GC.

Reed Copsey
I know, but don't think so in this case. When I add GC.Collect() at the beginning of the MouseDown handler, it should pick up the control eligible for garbage collection. It doesn't. I gather there still must be some reference to it internally in the Canvas object.
Bart Roozendaal
@Bart: Try adding GC.Collection at the END of the handler - The control is still in the UI at the beginning of the handler... Does that help?
Reed Copsey
@Bart: Also - Try adding a call to GC.WaitForPendingFinalizers() - See: http://msdn.microsoft.com/en-us/library/system.gc.waitforpendingfinalizers.aspx
Reed Copsey
It doesn't really matter. If you continue to click objects are created and removed all the time. None of those get released, but at the end of the program.
Bart Roozendaal
Tried adding WaitForPendingFinalizers: it doesn't help...
Bart Roozendaal
@Bart: Are you subscribing to events on your UserControl? If you have an event subscription, it will "root" your object...
Reed Copsey
Nop, the code above is all there is. It demonstrates the issue perfectly.
Bart Roozendaal
@Bart: See my edit - add this.UpdateLayout() (UserControl level) to force the UserControl/Window to release the element.
Reed Copsey
+1  A: 

Change

public class MyControl : UserControl

to

public class MyControl : ContentControl

and it will say goodbye (after the second time you remove the control.) I also verified the memory does not leak by using

Debug.WriteLine("mem: " + GC.GetTotalMemory(true).ToString());

Also, see this:

You remove the TestControl by clearing grid.Children, but it is not immediately eligible for garbage collection. Several asynchronous operations on it are pending, and it cannot be GC'd until those operations complete (these include raising the Unloaded event and some cleanup code in the rendering engine).

I verified that if you wait until these operations complete (say by scheduling a Dispatcher operation at ContextIdle priority), the TestControl becomes eligible for GC, independent of the presence of a binding on the TextBlock.

UserControl must either have a internal event that doesn't clean up quickly, or it might be a bug with VS2010 RC. I'd report this through connect, but for now switch to ContentControl.

Since you're using UserControl, I assume you'll also have to switch to using the Generic.xaml template. That isn't too difficult of a change-over (and for most things is a better solution.)

Joshua Blake
+2  A: 
Danny Varod
A: 

Hi

We had the same problem and also thought that this may be the reason. But we analyzed our project using Memory profiler tools and found that there is nothing to do with the Grid.Remove or Grid.RemoveAt. So i think my suggestion is just have a look your project at the memory profiler tool and see what is happening inside your project. hopes this helps.

Kishore Kumar