I have been reading up on WPF memory handling and have followed every top 5 and top 8 Memory leak Pitfalls, but nothing helps me in my current situation.
I have had an issue with my software where WPF won't release it memory until the program terminates. If I let it go forever it will cause an OutOfMemoryException no matter what I do. I have managed to isolate the issue within a small sample to show how it is not releasing its memory, even though I do not use it anymore. Here is how I can reproduce the problem:
I created 2 projects, one console program, and one WPF Application. In my WPF application I have a MainWindow.xaml which has nothing in it:
<Window x:Class="MemoryLeakWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MemoryLeakWpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="MainWindow_OnLoaded">
<Grid>
</Grid>
</Window>
I do subscribe to the Loaded event which I use to instantly close the window which can be seen in the .cs file here:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Debug.WriteLine("Constructing",GetType().Name);
}
~MainWindow()
{
Debug.WriteLine("Deconstructing", GetType().Name);
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
Close();
}
}
I also have added debug lines to my constructor and deconstructor so I can track when it is created and discarded. I then create a Controller class in the WPF application which represents the entry point to this WPF class library that has a method to create and show the window:
public class Controller
{
public void Execute()
{
MainWindow window = new MainWindow();
window.ShowDialog();
Debug.WriteLine("Constructing", GetType().Name);
}
~Controller()
{
Debug.WriteLine("Deconstructing", GetType().Name);
}
}
Here I also added debug track lines. I don't have an App.xaml as this WPF project is set as a Class Library in its properties. That is the WPF Part. In the console project I added the following code to my main class:
[STAThread]
static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
Controller controller = new Controller();
Console.WriteLine("Test " + i);
controller.Execute();
}
Console.WriteLine("Pressing enter will close this");
Console.ReadLine();
Debug.WriteLine("Party is over, lets leave");
}
So basically the setup is that I have a console class that wants to show a dialog. It creates the controller for the WPF application and calls Execute. The controller show the window that immediately closes when it has done loading. The console class then creates a new controller to do the process all over again. Now, this is what I see in my output:
MainWindow: Constructing
Controller: Constructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
The controller is constructing and deconstructing, but the window is not. However, when the for loop is complete and I press enter to let the program run out, I get this:
Party is over, lets leave
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
Suddenly all the instances of the MainWindow are now deconstructing, but only when the program runs out, not when we discard the reference in the for loop. This means that in our program we only have a finite number of times we can open the window before an OutOfMemoryException will occur.
But the million and a half dollar question is: How can I persuade WPF to release its memory while the program is running and not when the program closes?