1

I am working on a WPF application which working with very large binary datasets. However we are having huge memory leaks. It appears the garbage collector is never reclaiming the space and the Window remains in memory forever. This is leading to OOM issues.

Here is a snippet of code from our Login window which launches the MainWindow upon successful authentication.

I am explicitly setting mainWindow reference to null. However using dot Memory I can see there are multiple version in memory when we log out and log back in? This is happening all over the application, this is just one example.

var mainWindow = new MainWindow();
Hide();
mainWindow.ShowDialog();

if (mainWindow.LogoutOnClose)
{
     mainWindow.LogoutOnClose = false;
     OmegaApp.Instance.LogOff();
     mainWindow.Close();
     mainWindow = null;  // allow MainWindow to be garbage collected
     ShowDialog();
 }
 else
 {
     Application.Current.Shutdown();
 }

Edit: Some background, I am loading large amounts of binary data from database, so when the user closes that window I need to reclaim that memory. At times opening a Window is allocating 500MB or more.

Adding the following section in the closing event handler seems to have helped, but still doesn't explain why the window itself remains in memory.

private void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        // destroy render window
        _miniMainModelSession.DeleteRenderWindow();

        // dispose of native resources
        _miniRenderer.Dispose();
        _miniRenderer = null;
        PreviewRenderer.Dispose();

        // deallocate memory
        _dialog = null;
        _patientShapes = null;
        _miniMainModelSession = null;

        DialogResult = _cancelClose;
        OmegaApp.Instance.UnregisterControl("PatientFolder");

        // collect any garbage explicity 
        GC.Collect();
    }
greyfox
  • 6,426
  • 23
  • 68
  • 114

2 Answers2

0

You should link an owner to your dialog window. This will allow the program to remember the link to the dialog window. This makes it possible for the program te reuse the resources of the previous version of the dialog window. Worked for me. In my case, the dialog was started from a winform main UI. So I had to create a static wpf owner window.

Preparation:

private static Window _wpfEmptyOwnerWindowToPreventMemoryLoss = null;

public Window WpfEmptyOwnerWindowToPreventMemoryLoss()
{
    if (_wpfEmptyOwnerWindowToPreventMemoryLoss==null)
    {
        _wpfEmptyOwnerWindowToPreventMemoryLoss = new Window();
        _wpfEmptyOwnerWindowToPreventMemoryLoss.Show();
        _wpfEmptyOwnerWindowToPreventMemoryLoss.Hide();
    }
    return _wpfEmptyOwnerWindowToPreventMemoryLoss;
}

public class WpfSelectFromList : Window
{
    public WpfSelectFromList()
    {
        Owner = Tools.WpfEmptyOwnerWindowToPreventMemoryLoss();
    }
    public bool Select(...)
    {
        ....
        ShowDialog();
        return ...;
    }
}
Pascal
  • 1
0

Pure guesses:

  • OmegaApp.Instance.UnregisterControl("PatientFolder"); smells, but this line is safe. It smells to me because it looks like you are integrating with something "Omega", on UI layer ('unregistercontrol'?!) that most probably will hold a reference to your objects (window or just a control from that window) and keep them in memory (either window, or the control, which in turn pins the window)

  • after checking that, next thing to check is ... all event handlers. If your Window (or just about any your object) was ever attached to an event from anything else not related to that Window (Omega?), like, myWebClient.QueryFinished += myObj.onQueryFinished, then most probably THIS is what holds your window. You must deregister all event handlers bound to any sources that live longer than your object, because each of those handlers remember a pair: METHOD+TARGET, where 'target' is your object that provides the method to be called when event occurs..

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107