【发布时间】:2016-07-08 13:31:46
【问题描述】:
更新:澄清一下,这是一个关于内存泄漏的问题,而不是关于 UI 未能刷新以反映新的 null DataContext(该部分工作正常)。
看来,如果您将ItemsControl 的DataContext 设置为空,它将继续持有对其旧数据上下文的引用,直到您为其分配一个不为空的新数据上下文。您可以使用问题底部的代码确认这一点。单击“将 DataContext 设置为 null”,然后根据需要多次单击“收集垃圾”。 “Foo finalized”消息将永远不会出现。然后点击“Set DataContext to empty object”,再次收集垃圾,马上就可以看到finalizer运行了。
由于错误地假设我的一个ItemsControls(DataGrid)会在TheGrid.DataContext = null 之后释放其对旧数据上下文的所有引用,因此我的应用程序发生了内存泄漏。为什么不呢?这是预期的行为吗?
更新:点击“Set DataContext to null”后VS2015诊断工具提供的root路径如下所示。在Foo 本身来自我的代码之后,我什么都没想:
FooListDictionary+DictionaryNodeListDictionaryListDictionary+DictionaryNodeListDictionaryHybridDictionary-
Object[][强力手柄]
XAML:
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ItemsControl ItemsSource="{Binding Foos}" />
<Button Content="Set DataContext to null" Click="SetDataContextToNullClicked"/>
<Button Content="Set DataContext to empty object" Click="SetDataContextToEmptyObjectClicked" />
<Button Content="Collect garbage" Click="CollectGarbageClicked"/>
</StackPanel>
</Window>
C#:
namespace WpfApplication
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
public class Foo
{
~Foo()
{
Debug.Print("Foo finalized");
}
}
public class FoosViewModel // implementing INotifyPropertyChanged here doesn't help
{
public FoosViewModel(IEnumerable<Foo> foos)
{
Foos = foos;
}
public IEnumerable<Foo> Foos { get; }
}
public partial class MainWindow
{
public MainWindow()
{
DataContext = new FoosViewModel(Enumerable.Repeat(new Foo(), 1));
InitializeComponent();
}
private void SetDataContextToNullClicked(object sender, RoutedEventArgs e)
{
DataContext = null;
}
private void SetDataContextToEmptyObjectClicked(object sender, RoutedEventArgs e)
{
DataContext = new FoosViewModel(Enumerable.Empty<Foo>());
}
private void CollectGarbageClicked(object sender, RoutedEventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
【问题讨论】:
-
在绑定
null之后尝试刷新null吗?TheGrid.Items.Refresh(); -
@SeM 结果还是一样——来自旧 DC 的终结器在我分配非空值之前不会运行。
-
我不确定你在做什么,但是使用上面的代码,只要我单击设置为 null,我的 DataGrid 就会清除。这不会发生在你身上吗?
-
@d.moncada 是的,网格已清除,但它仍然在某处保留旧 DataContext 中对
IEnumerable<Foo>的引用,这意味着Foos 的整个列表已泄露(有在这个玩具示例中只有一个)。 -
"如果绑定到一个没有实现 INotifyCollectionChanged 接口的集合,WPF 会创建一个对该集合的强引用。因此,它会留在内存中"jetbrains.com/help/dotmemory/10.0/Inspections.html
标签: c# .net wpf memory-leaks itemscontrol