简体   繁体   English

最简单的带有DependencyProperty的WPF UserControl中的内存泄漏

[英]Memory leak in the simplest WPF UserControl with DependencyProperty

So I am trying to create a WPF control that has one new dependency property and uses this DP. 因此,我试图创建一个具有一个新的依赖项属性并使用此DP的WPF控件。 The control's XAML is as simple as this: 控件的XAML就是这样简单:

<UserControl x:Class="DPTest.TestControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:DPTest">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=local:TestControl}, Path=TestText}"/>

Just a TextBlock displaying the new property. 只是一个显示新属性的TextBlock。 Codebehind declares the dependency property like this: Codebehind声明依赖项属性,如下所示:

using System.Windows;

namespace DPTest
{
  public partial class TestControl
  {
    public static readonly DependencyProperty TestTextProperty = 
      DependencyProperty.Register("TestText", typeof(string), typeof(TestControl), new PropertyMetadata(default(string)));

    public TestControl()
    {
      InitializeComponent();
    }

    public string TestText
    {
      get => (string) GetValue(TestTextProperty);
      set => SetValue(TestTextProperty, value);
    }
  }
}

Everything is ok up to this point. 到目前为止,一切正常。 But, being created this control never gets collected by GC. 但是,创建此控件后,GC永远不会收集它。 To demonstrate this I use a Window which simply creates tons of TestControls on loading: 为了演示这一点,我使用了一个Window,该窗口在加载时简单地创建了大量的TestControls:

using System.Windows;

namespace DPTest
{
  public partial class MainWindow
  {
    public MainWindow()
    {
      InitializeComponent();
      Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
      while (true)
      {
        var uc = new TestControl();
      }
    }
  }
}

Opening this window shows RAM rapidly increasing, while GC firing quite often. 打开此窗口显示RAM快速增加,而GC频繁触发。 Memory profiling shows all the instances of TestControl sitting in RAM. 内存分析显示TestControl的所有实例都位于RAM中。

If I replace binding inside the control with constant value so it is just like this: 如果我用恒定值替换控件内的绑定,则它是这样的:

<TextBlock Text="hello"/>

RAM does not increase and the unused control instances get collected successfully. RAM不会增加,并且未使用的控制实例将被成功收集。

Looks like I'm doing something wrong but the example is so simple and common so I got stuck. 看起来我做错了事,但是示例是如此简单和常见,以至于我陷入了困境。 Please help. 请帮忙。

You're dominating the dispatcher thread, which prevents the data binding engine from doing anything. 您正在控制调度程序线程,这将阻止数据绑定引擎执行任何操作。 If I were to hazard a guess, something is being queued up to be handled by the data binding engine at the DataBind dispatcher priority, and your loop is preventing that from happening. 如果要冒昧地猜测一下,数据绑定引擎将以DataBind调度程序的优先级来处理某些事情,而您的循环会阻止这种情况的发生。

If you rewrite it like so, you will still generate an endless number of test controls, but you won't see your memory grow: 如果以这种方式重写它,您仍然会生成无数的测试控件,但是您不会看到内存增加:

private void OnLoaded(object sender, RoutedEventArgs e)
{
    InstantiateNewControl();
}

private void InstantiateNewControl()
{
    var tc = new TestControl();
    this.Dispatcher.BeginInvoke(
        DispatcherPriority.Background,
        new Action(InstantiateNewControl));
}

This will give the data binding engine an opportunity to do some work between each instantiation. 这将使数据绑定引擎有机会在每个实例之间进行一些工作。

If you comment out the line specifying DispatcherPriority.Background , you'll see the memory start to grow again. 如果注释掉指定DispatcherPriority.Background的行,则会看到内存再次开始增长。 That's because the default priority is the highest ( Normal ), which takes precedence over layout, input, data binding, etc. By flooding the dispatcher at a high priority, you're preventing other work from being done. 这是因为默认优先级是最高( Normal ),它优先于布局,输入,数据绑定等。通过以高优先级泛滥调度程序,您可以防止其他工作完成。


Update 更新

After investigating with a memory profiler, it looks like the memory growth is stemming from the new BindingExpression instances being kept alive in DataBindManager . 在使用内存分析器进行调查之后,内存增长似乎是由于新的BindingExpression实例在DataBindManager中保持活动状态而引起的。 Specifically, there is a dictionary that maps the binding expressions to their most recent data binding operations, and it looks like you're preventing that dictionary from being properly maintained. 具体来说,有一个字典将绑定表达式映射到它们的最新数据绑定操作,并且看起来您正在阻止该字典的正确维护。 The expressions on the newly instantiated test controls never get removed. 新实例化的测试控件上的表达式永远不会被删除。

this has nothing to do with your control at all. 这完全与您的控制无关。 your while-loop in the loaded event runs forever. 您在while事件中的while循环将永远运行。 the garbage collector simply cannot collect as fast as you are creating new controls. 垃圾收集器无法像创建新控件那样快速收集。

try this with a foreach-loop and a few thousand iterations - that should at least get you up and running 尝试使用foreach循环和数千次迭代-至少应该可以使您正常运行

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM