【问题标题】:AvalonDock DocumentContent not garbage collectedAvalonDock DocumentContent 没有被垃圾收集
【发布时间】:2011-07-15 09:55:43
【问题描述】:

我创建了一个使用 AvalonDock 框架的应用程序。一个关键部分是使用AvalonDock.DocumentContent 派生编辑器编辑域模型实体的能力。我遇到了一个问题,发现我的编辑器在关闭并从 DockingManager.Documents 集合中删除后没有被垃圾回收。

经过一番徒劳的搜索后,我创建了一个可以通过以下方式重新创建的小型测试应用程序:

  • 在 Visual Studio(我使用的是 2008)中,创建一个名为 AvalonDockLeak 的新 WPF 应用程序;
  • 添加对AvalonDock库的引用(我的版本是1.3.3571.0);
  • 添加一个名为Document 的新用户控件;
  • 将 Document.xmal 更改为:

    <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>
    
  • 将 Document.xmal.cs 更改为:

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

    我添加的析构函数能够诊断问题,在打开 { 的方法上添加断点,并查看它是否被命中。它总是在关闭测试应用程序时这样做,但不是更早。

  • 现在将 Window1.xaml 更改为:

    <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>
    
  • 将 Window1.xaml.cs 更改为:

    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();
            }
        }
    }
    

这个简单的应用程序也包含泄漏。这可以通过~Document() 开头的断点观察到{关闭DocumentContent 后没有被命中。

现在我想要的是,这是一个已知问题吗?有没有办法防止它?如果对象只是在很长一段时间后才被垃圾收集,那么我能做些什么来加快这一点?顺便说一下,调用 GC.Collect() 也无济于事。

【问题讨论】:

  • 检查 Avalon 源代码,看看 document.Show(this.DockManager); 做了什么。我猜该文件以某种方式向经理注册,并且没有正确取消注册。 DockManager 上是否有删除文档的方法?
  • 它只是做manager.Documents.Add(this);。关闭文档后,它也不再存在于 manager.Documents 集合中。
  • 嗯,找出答案的一种方法是附加内存分析器或进行内存转储并使用 Windows 调试工具来找出持有引用的内容。

标签: c# wpf garbage-collection avalondock


【解决方案1】:

显然,您的 DocumentContent 的引用由事件处理程序保存在某处。你应该使用像微软的CLR-Profiler 这样的内存分析器来确定原因。

您应该注意始终取消注册已注册的活动。否则你会得到内存泄漏。为此,您可以使用 -= 运算符。

【讨论】:

    【解决方案2】:

    DocumentContent 默认在关闭时隐藏,这意味着引用保持活动状态。

    如果您希望 DocumentContent 关闭并随后处置,您需要在派生的 DocumentConcent 中指定几个属性或修改 AvalonDock 源。

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

    现在关闭时,它会处理引用而不是挂在它上面,因为它只是被隐藏了。

    【讨论】:

    • DocumentContent-Class 中不存在 HideOnClose-Property(仅在 DockableContent 中)!我有和开线器一样的问题。如果我调试派生的 DocumentContent-Class,我可以看到,如果您单击 Clsoing-Symbol,则会调用 OnClosing()-Handler,但不会调用该类的 Desctructor。那么如何关闭 DocumentContent-Instance?
    • @0xDEADBEEF 你说得对,DocumentContent 不包含HideOnClose 属性。您是否尝试过继续添加 DocumentContent 实例,然后关闭它们,多次执行此操作并通过内存分析器观察内存是否不断增加或 GC 是否运行并回收内存?暂时忽略未调用的析构函数。
    • 是的。我已经打开了 50 个实例。我可以在堆中找到它们。我关闭了它们并打开了 100 多个。我已经这样做了 2 次。并且它们都(250 个实例)保留在堆中-> 内存不会减少。手动调用 GC.Collect() 没有帮助
    • 最有可能的情况是 DocumentContent 存在于某个集合中,但 OP 声明 manager.Documents 在关闭时不包含 DocumentContent。让我看看我能找到什么,会环顾四周。
    【解决方案3】:

    我强烈建议您和任何使用 AvalonDock 1.3 的人升级到 2.0 版。 最新版本是 MVVM 友好的,不会遇到这个问题(文档和锚点被正确地垃圾收集)。 更多信息:avalondock.codeplex.com

    谢谢

    【讨论】:

    • 似乎没有从 1.3 到 2.0 的简单升级路径 - 例如,布局元素不再派生自 FrameworkElement 类。不过 MVVM 看起来不错 :-)
    • 不,这并不容易,但它确实成功了,而且还有更多。伟大的工作@adospace。
    【解决方案4】:

    我在这个方向上也遇到了问题。关闭选项卡会导致内存泄漏。我用分析器检查了它,结果发现 ActiveContent 仍然会保留引用,从而阻止 GarbageCollector 启动。

    我关闭标签的代码:

    dc // DocumentContent, I want to close it
    documentPane // DocumentPane, containing the dc
    
    documentPane.Items.Remove(dc);
    

    这完成了关闭选项卡的工作,但得知我需要调用

    dc.Close();
    

    如果我希望将 ActiveContent 设置为 null 并让 GC 完成工作,则在从 documentPane 中删除内容之前。

    注意:我使用的是 1.2 版的 AvalonDock,这可能在新版本中有所改变。

    【讨论】:

      【解决方案5】:

      这看起来像是一个长期未解决的错误...:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-18
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多