icsharpcode / WpfDesigner

The WPF Designer from SharpDevelop
MIT License
949 stars 251 forks source link

Memory leak: This class and / or its base has a memory leak #91

Closed boelew closed 1 year ago

boelew commented 2 years ago

In our application we use the following code to initialize a DesignSurface:

using (var xmlReader = XmlReader.Create(new StringReader(XamlText)))
{
    var settings = new XamlLoadSettings();
    DesignSurface.LoadDesigner(xmlReader, settings);
}

When we close the window that contains this DesignSurface we initially had code like the following:

DesignSurface.UnloadDesigner();
DesignSurface = null;

We expect that after closing the window all related objects are disposed. This is not the case and in the end we came to the conclusion (by uncommenting lines of code) that this class or its base keeps references to objects that are not released. We copied the code of OutlineNode class into our own CustomOutlineNode class to find where the memory leak occurs. However, we cannot completely track it down.

In the following section we see a static variable that is never cleaned up. We supposed to ourselves that this caused the issue. https://github.com/icsharpcode/WpfDesigner/blob/c7f0ff74e29b0ec4618ab9129d28e79aa3e3b986/WpfDesign.Designer/Project/OutlineView/OutlineNode.cs#L29-L35

Then in our own CustomOutlineNode class we used the base constructor with string parameter instead of the constructor with DesignItem parameter, because we found that a problem maybe exist in the base class as well. So our constructor looks like this:

protected CustomOutlineNode(DesignItem designitem)
{ 
   DesignItem = designitem;

    UpdateChildren();
    base.SelectionService.SelectionChanged += Selection_SelectionChanged;
}

Here we are stuck. When we comment out the lines 'DesignItem = designitem; ' and 'UpdateChildren();' then Garbage collection works fine. So we conclude that at least DesignItem is not cleaned up well. DesignItem is initialized here: https://github.com/icsharpcode/WpfDesigner/blob/c7f0ff74e29b0ec4618ab9129d28e79aa3e3b986/WpfDesign.Designer/Project/OutlineView/OutlineNodeBase.cs#L51-L54

And probably also the static variable 'outlineNodes' is not cleaned up well.

We added the following methods to our CustomOutlineNode class to test all of our conclusions above:

public void Clean()
{
    base.SelectionService.SelectionChanged -= Selection_SelectionChanged;
    base.DesignItem.Remove();
    base.DesignItem = null;
    base.Children.Clear();
}

public static void Destroy()
{
    foreach (var outlineNode in outlineNodes)
    {
        ((CustomOutlineNode)outlineNode.Value).Clean();
    }

    OutlineNodeBase.DummyPlacementType = null;
    outlineNodes.Clear();
}

And then when we close our window we try to cleanup using the following code:

protected override void OnClosed() // just an override method from our own framework
{
    DesignSurface.DesignContext.Services.Component.ComponentRegisteredAndAddedToContainer -= Component_ComponentRegisteredAndAddedToContainer;
    DesignSurface.DesignContext.Services.Component.PropertyChanged -= Component_PropertyChanged;
    UndoService.UndoStackChanged -= UndoService_UndoStackChanged;
    DesignSurface.UnloadDesigner();
    DesignSurface = null;
    CustomOutlineNode.Destroy(); // our new Destroy method
    OutlineRoot = null;

    base.OnClosed();
}
LukeOrdelmans commented 1 year ago

should be fixed by #96

boelew commented 1 year ago

Thanks, this issue is fixed indeed.