0

I have a very bad performance issue. I´m currently working on a multi-window application, but when I close one of the windows no memory is deallocated.

There are even controls I instantiate in code and after I used them I assigned null to their fields but nothing. The GC refuses to collect the control although there is no reference on it any more.

Googling that problem brought me to Application.Exit and Dispatcher.ShutdownStarted but they do not solve my problem, cause the application keeps on running.

I made a small sample to show you my problem:

MainPage.xaml

    <Window x:Class="FinalizerSampleProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:FinalizerSampleProject"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Button Content="Get rid of the custom control!" Grid.Row="1" Click="Button_Click" x:Name="DummyButton" />
    </Grid>
</Window>

MainPage.xaml.cs

using System;
using System.Windows;

namespace FinalizerSampleProject
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private CustomControl control = new CustomControl();

    public MainWindow()
    {
        InitializeComponent();
        LayoutRoot.Children.Add(control);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (control != null)
        {
            LayoutRoot.Children.Remove(control);
            control = null;
            DummyButton.Content = "Show how many instances of CustomControl still exist.";
            GC.Collect();
        }
        else
        {
            MessageBox.Show(CustomControl.InstanceCounter.ToString());
        }
    }
}

}

CustomControl.xaml

 <UserControl x:Class="FinalizerSampleProject.CustomControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Background="LightGreen">
        <TextBlock Text="Hi, I'm a custom control!"/>
</UserControl>

CustomControl.xaml.cs

using System.Windows.Controls;

namespace FinalizerSampleProject
{
/// <summary>
/// Interaction logic for CustomControl.xaml
/// </summary>
public partial class CustomControl : UserControl
{
    public static int InstanceCounter = 0;
    public CustomControl()
    {
        InstanceCounter++;
        InitializeComponent();
    }

    ~CustomControl()
    {
        InstanceCounter--;
    }
}
}

If you try it, you will see that this counter every time appears to be "one". I have no idea how to get rid of that object instance.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
christoph
  • 1,019
  • 1
  • 12
  • 23
  • You have to remember that first the `GC` won't be triggered immediately after you invoke `GC.Collect()`. You should see it more a "suggestion" to `GC` rather than a direct instruction. Secondly, the destructor (or finalize method) isn't executed on the first `GC` run. When `GC` spots an object with the finalize method it only adds it to the f-reachable collection, moves the object to a dormant state and on the second run runs the finalize method. That's why you see the `InstanceCounter` being set to 1. – PiotrWolkowski Jun 13 '14 at 10:31
  • @PiotrWolkowski ok, but when does the GC invoke the finalize method? I can wait for minutes and it is not invoked. – christoph Jun 13 '14 at 10:38

1 Answers1

1

NOTE: This answer would only apply if the question was about the WinForms UserControl.


Your custom controls are probably disposed and "freed" as they should. It's just that your finalizer code isn't invoked because when a UserControl is disposed it tells the garbage collector to NOT invoke its finalizer.

This is the standard Dispose implementation that suppress finalizers from being called:

public void Dispose()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

That code comes from System.ComponentModel.Component. And your CustomControl class derive from that via UserControl.

The line GC.SuppressFinalize(this) is what prevents your counter from decrementing. No worries though, the instances are disposed (and collected by the GC).

Try this instead:

public static int InstanceCounter = 0;
private bool disposed;

public CustomControl()
{
    InstanceCounter++;
    InitializeComponent();
}

protected override void Dispose(bool disposing)
{
    if (disposing && !this.disposed)
    {
        InstanceCounter--;
        this.disposed = true;
    }
}
Mårten Wikström
  • 11,074
  • 5
  • 47
  • 87