【发布时间】:2011-09-25 07:18:24
【问题描述】:
我正在编写一个使用一堆双向绑定的程序,使用的内存量已成为一个大问题。在我的完整应用程序中,我从 50Mb 开始,然后,仅通过使用绑定(即更改一侧的值并让绑定更新另一侧),我通常会打破 100Mb,即使我的代码没有分配任何新内容.我的问题是这个额外的内存是什么以及我可以做些什么来控制它。我在下面创建了一个简单、可重现的示例:
假设我有一个包含以下内容的主窗口:
<StackPanel Height="25" Orientation="Horizontal">
<TextBox UndoLimit="1" Name="TestWidth" />
<Label>,</Label>
<TextBox UndoLimit="1" Name="TestHeight" />
</StackPanel>
然后在这个窗口的构造函数中,我生成一个新窗口,显示它,然后将它的 WidthProperty 和 HeightProperty 依赖属性绑定到利用 INotifyPropertyChanged 的变量:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private int _WidthInt;
public int WidthInt
{
get { return _WidthInt; }
set { _WidthInt = value; NotifyPropertyChanged("WidthInt"); }
}
private int _HeightInt;
public int HeightInt
{
get { return _HeightInt; }
set { _HeightInt = value; NotifyPropertyChanged("HeightInt"); }
}
public MainWindow()
{
InitializeComponent();
Window testWindow = new Window();
testWindow.Show();
Binding bind = new Binding("HeightInt");
bind.Source = this;
bind.Mode = BindingMode.TwoWay;
testWindow.SetBinding(Window.HeightProperty, bind);
//bind.Converter = new convert();
//this.TestHeight.SetBinding(TextBox.TextProperty, bind);
bind = new Binding("WidthInt");
bind.Source = this;
bind.Mode = BindingMode.TwoWay;
testWindow.SetBinding(Window.WidthProperty, bind);
//bind.Converter = new convert();
//this.TestWidth.SetBinding(TextBox.TextProperty, bind);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string sProp)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sProp));
GC.Collect();
}
}
然后,如果我不断调整窗口大小,我在任务管理器中的内存使用量线性增加,没有明显的上限。该程序从 17Mb 开始,在调整大小后 30 秒内增加到 20Mb 并在 20 的某个点后悬停(感谢 Ian)。即使没有绑定,这实际上也会发生,并且内存不会回退。虽然很烦人,但这不是我所说的“内存飞跃”。
如果我取消注释也将文本框绑定到变量的行,我会得到以下结果:在短短几秒钟内,它从 18Mb 跳转到 38Mb,然后悬停在那里(请注意在 XAML 中设置文本框的绑定不影响内存尖峰)。我尝试为文本框绑定实现自己的转换器,但这不会影响内存使用。
如果我将变量更改为新的依赖属性并绑定到它们,跳转仍然存在,例如
public static readonly DependencyProperty WidthIntProperty = DependencyProperty.Register("WidthIntProperty", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0, null));
int WidthInt
{
get { return (int)this.GetValue(WidthIntProperty); }
set { this.SetValue(WidthIntProperty, value); }
}
...
Binding bind = new Binding("Text");
bind.Source = TestHeight;
bind.Mode = BindingMode.TwoWay;
this.SetBinding(MainWindow.HeightIntProperty, bind);
testWindow.SetBinding(Window.HeightProperty, bind);
或者如果我直接在 text 属性和 width 依赖属性之间绑定并使用 BindingMode.OneWay 或反之亦然。
使用 CLR 分析器似乎无法向我显示正在分配的内容,而且我无权访问商业内存分析器。有人可以向我解释一下内存中保存了什么以及如何在仍然具有连续 BindingMode 的功能的同时摆脱它吗?我是否必须实现自己的绑定方法并自己处理事件?或者有什么我可以定期在 GC 之外冲洗的吗?
感谢您的宝贵时间。
【问题讨论】:
-
在 GC.Collect 未注释的情况下,我无法重现您第一个场景的“继续”部分。在调试器之外运行 Release 构建,它达到大约 24,700K,然后在此附近波动,但不会超过该值。同样,您的第二个场景(绑定文本框)也稳定下来。离开 GC.Collect 注释掉,它仍然稳定,只是在一个更高的水平。 (我的系统上大约有 34MB。)我没有看到持续增加。必须缺少一些细节才能重现这一点。您是在运行发布还是调试?
-
感谢您尝试重现我的结果,Ian。 (编辑:ack,我没有意识到输入自动提交而不是添加新行...)我在 Visual C# 中运行调试,doh!在取消注释 GC.Collect 并在环境之外运行发布后,它从 15.5Mb 开始并稳定在 17.5Mb,即使没有绑定 (!) 也会发生这种情况。如果我取消注释也绑定 TextBoxes 的行,我会从 15.8Mb 开始,它实际上确实达到了 33.7Mb!这显然不是内存泄漏,但我仍然对所有这些额外内存有疑问。我会更新问题以反映这一点。
标签: wpf data-binding memory-leaks dependency-properties inotifypropertychanged