Search code examples
wpfimageuser-controlswindowdispose

WPF OutOfMemory Exception after displaying images a couple of times


I created a voting system using wpf and capitalizing the wpf container ship model. In the system, a series of user controls that displays the candidates pictures is being used. after the end-user finishes voting, the system will automatically log out and wait for the next one to log in. After several consecutive vote castings, the pictures start to get missing one by one randomly. When I checked, it was because of the out of memory exception. As I continue to stress test it, more and more pictures start to disappear. I have a hunch it was because the system doesn't dispose the previous windows and user controls and pictures that has been displayed. What can I do to dispose the window or at least make sure the pictures that has been displayed be disposed after the closing of the window?


Solution

  • A few things you can do. First would be use a memory profiler. I personally love RedGate ANTS Memory Profiler it will tell you if your objects are getting cleaned up and I believe they have a free trial you can use.

    Also how are your images being displayed? Does the window load them from disk? If there are a small number of images, I would suggest putting them in a ResourceDictionary like so:

    <BitmapImage UriSource="/MyApp.Client;component/Images/Pinned.png" x:Key="Pinned" PresentationOptions:Freeze="True" />
    <BitmapImage UriSource="/MyApp.Client;component/Images//Unpinned.png" x:Key="Unpinned" PresentationOptions:Freeze="True" />
    

    Then in your XAML you can use:

    <Image Source="{StaticResource: Unpinned}"/>
    

    This will ensure your image is loaded only once. Everything that needs it gets a reference to it.

    Also remember if you have any events subscribed on your window it can prevent your window from closing. You should always dereference your events when the window is closing. For instance:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
            Loaded += DoStuff();
    
        }
    
        private RoutedEventHandler DoStuff()
        {
            //some code here
        }
    
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);
    
            Loaded -= DoStuff();
        }
    }
    

    Beware generic methods. For instance if you did this:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
    
            Loaded += (o, e) =>
            {
                //Do stuff here
            };
    
        }
    
    }
    

    There is no way to dereference that because that method is generic so there is nothing to put in your closing to get rid of it. This is a very common problem for memory leaks and preventing windows from closing.

    Another common one often overlooked is the Timer class. If you start a timer and then close the window without stopping the timer, the Timer class will hold a reference to your window preventing it from being closed. Always ensure your timers are stopped when the window closes.