AvalonDock DocumentContent not garbage collected
Asked Answered
C

5

8

I have created an application that makes use of the AvalonDock framework. A key part is the ability to edit domain-model entities using AvalonDock.DocumentContent derived editors. I hit upon a problem and discovered the my editors are not being garbage collected after they are closed and removed from the DockingManager.Documents collection.

After some fruitless searching I created a small test application that can be recreated in the following manner:

  • In Visual Studio (I'm using 2008), create a new WPF application called AvalonDockLeak;
  • Add a reference to the AvalonDock library (my version is 1.3.3571.0);
  • Add a new UserControl called Document;
  • Change Document.xmal to:

    <ad:DocumentContent x:Class="AvalonDockLeak.Document"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock">
        <Grid>
            <TextBox />
        </Grid>
    </ad:DocumentContent>
    
  • Change Document.xmal.cs to:

    namespace AvalonDockLeak
    {
        using AvalonDock;
    
        public partial class Document : DocumentContent
        {
            public Document()
            {
                InitializeComponent();
            }
    
            ~Document()
            {
            }
        }
    }
    

    The destructor I have added to be able to diagnose the problem adding a breakpoint on the methods opening {, and seeing if it gets hit. It always does on closing the test application but not earlier.

  • Now change Window1.xaml to:

    <Window x:Class="AvalonDockLeak.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock"
            Title="Memory Leak Test" Height="300" Width="300">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Button Name="NewButton" Click="NewButton_Click" Content="New" Height="26" Width="72" />
            <ad:DockingManager x:Name="DockManager" Grid.Row="1">
                <ad:DocumentPane />
            </ad:DockingManager>
        </Grid>
    </Window>
    
  • Change Window1.xaml.cs to:

    namespace AvalonDockLeak
    {
        using System.Windows;
    
        public partial class Window1 : Window
        {
            private int counter = 0;
    
            public Window1()
            {
                InitializeComponent();
            }
    
            private void NewButton_Click(object sender, RoutedEventArgs e)
            {
                string name = "Document" + (++this.counter).ToString();
                var document = new Document()
                {
                    Name = name,
                    Title = name,
                    IsFloatingAllowed = false
                };
    
                document.Show(this.DockManager);
                document.Activate();
            }
        }
    }
    

This simple application also contains the leak. Which can be observed by the breakpoint on the ~Document() opening { not getting hit after closing a DocumentContent.

Now what I want to now is, is this a known problem and is there a way to prevent it? If the objects are only garbage collected after a long time then what can I do to expedite this? Calling GC.Collect() does not help by the way.

Cherilyncherilynn answered 15/7, 2011 at 9:55 Comment(3)
Check the Avalon source code and see what document.Show(this.DockManager); does. I guess the document registers itself in some way with the manager and it's not being unregistered properly. Is there a method on the DockManager to remove documents?Masseter
It simply does manager.Documents.Add(this);. After the document is closed it is also no longer present in the manager.Documents collection.Cherilyncherilynn
Well, one way to find out is to attach a memory profiler or make a memory dump and use debugging tools for windows to find out what's holding the references.Masseter
D
1

I strongly suggest you and anyone using AvalonDock 1.3 to upgrade to version 2.0. Latest version is MVVM-friendly and doesn't suffer of this issue (Documents and Anchorables are correctly garbage collected). More info: avalondock.codeplex.com

Thanks

Decembrist answered 18/11, 2012 at 21:44 Comment(2)
There doesn't seem to be an easy upgrade path from 1.3 to 2.0 - for example, layout elements no longer derive from FrameworkElement class. MVVM looks good though :-)Icarian
No, it isn't easy but it did the trick plus some more. Great work @adospace.Cherilyncherilynn
L
5

obviously the references of your DocumentContent is kept by an eventhandler somewhere. you should use a memory-profiler like CLR-Profiler from microsoft to determine the cause.

you should take care that you always deregister an registered event. otherwise you can get a memory leak. for this you can use the -= operator.

Leveret answered 6/1, 2012 at 21:45 Comment(0)
S
3

The DocumentContent will by default hide on close, which means the reference is kept alive.

If you want the DocumentContent to close and subsequently dispose you need to specify a couple of properties within a derived DocumentConcent or modify the AvalonDock source.

        this.IsCloseable = true;
        this.HideOnClose = false;

Now when closed, it will dispose of the reference versus hanging on to it as it was merely being hidden.

Syracuse answered 30/11, 2011 at 17:1 Comment(4)
The HideOnClose-Property does not exist in DocumentContent-Class (only in DockableContent)! i have the same problem like the thread-opener. if i debug the derived DocumentContent-Class i can see that if you click on the Clsoing-Symbol the OnClosing()-Handler is called, but not the Desctructor of the class. so how to close a DocumentContent-Instance?Pie
@Pie You are correct that DocumentContent does not contain the HideOnClose property. Have you tried to continue adding DocumentContent instances problematically and then close them, do this multiple times and watch via memory profiler if the memory constantly creeps up or does the GC run and reclaim the memory? Ignore the destructor not being called for now.Syracuse
Yes. I´ve opened 50 instances. i can find them in the Heap. i closed them and opened 100 more. i´ve done this 2 times. and they´re all (250 instances) remained in the heap -> the memory doesn´t decrease. calling GC.Collect() manually does not helpPie
The most likely scenario is the DocumentContent existing in a collection somewhere however the OP stated that the manager.Documents does not contain the DocumentContent upon close. Let me see what I can find, will look around.Syracuse
B
1

I also had a problem in this direction. Closing tabs would cause memory leaks. I checked it with a profiler and it turned out that the ActiveContent would still keep a reference, preventing the GarbageCollector to kick in.

my code for closing the tab:

dc // DocumentContent, I want to close it
documentPane // DocumentPane, containing the dc

documentPane.Items.Remove(dc);

this did the job of closing the tab, but learned that I need to call

dc.Close();

before removing the content from the documentPane if I want ActiveContent to be set to null and let the GC do it's job.

Note: I use version 1.2 of AvalonDock, this may have changed in newer versions.

Baccarat answered 17/1, 2012 at 10:38 Comment(0)
A
1

This looks like a long time outstanding bug...:

http://avalondock.codeplex.com/workitem/9113

Anglicism answered 24/2, 2012 at 14:29 Comment(0)
D
1

I strongly suggest you and anyone using AvalonDock 1.3 to upgrade to version 2.0. Latest version is MVVM-friendly and doesn't suffer of this issue (Documents and Anchorables are correctly garbage collected). More info: avalondock.codeplex.com

Thanks

Decembrist answered 18/11, 2012 at 21:44 Comment(2)
There doesn't seem to be an easy upgrade path from 1.3 to 2.0 - for example, layout elements no longer derive from FrameworkElement class. MVVM looks good though :-)Icarian
No, it isn't easy but it did the trick plus some more. Great work @adospace.Cherilyncherilynn

© 2022 - 2024 — McMap. All rights reserved.