问题 在WPF中:Children.Remove或Children.Clear不释放对象
更新:我在另一台安装得更干净的机器上尝试了这个。我无法在那台机器上重现这一点。如果我发现有什么违规(VSStudio)组件导致这种情况,我会告诉你。
我从后面的代码创建一些UIElements,并期待垃圾收集来清理东西。但是,在我预期的时候,对象不会被释放。我期待他们在RemoveAt(0)被释放,但他们只在程序结束时被释放。
从画布的儿童系列中删除时,如何释放对象?
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
MouseDown="Window_MouseDown">
<Grid>
<Canvas x:Name="main" />
</Grid>
</Window>
背后的代码是:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
GC.Collect(); // This should pick up the control removed at a previous MouseDown
GC.WaitForPendingFinalizers(); // Doesn't help either
if (main.Children.Count == 0)
main.Children.Add(new MyControl() { Background = Brushes.Yellow, Width = 100, Height = 50 });
else
main.Children.RemoveAt(0);
}
}
public class MyControl : UserControl
{
~MyControl()
{
Debug.WriteLine("Goodbye");
}
}
6524
2018-03-29 22:31
起源
答案:
更改
public class MyControl : UserControl
至
public class MyControl : ContentControl
并且它将说再见(在第二次移除控件之后。)我还通过使用验证了内存不泄漏
Debug.WriteLine("mem: " + GC.GetTotalMemory(true).ToString());
另外,请参阅 这个:
您通过清除grid.Children删除TestControl,但它不能立即符合垃圾回收的条件。它上面的几个异步操作正在等待,并且在这些操作完成之前不能进行GC操作(这些操作包括在渲染引擎中引发Unloaded事件和一些清理代码)。
我验证了如果等到这些操作完成(比如通过在ContextIdle优先级上安排Dispatcher操作),TestControl就有资格获得GC,而不管TextBlock上是否存在绑定。
UserControl必须具有不能快速清理的内部事件,或者它可能是VS2010 RC的错误。我通过connect报告这个,但是现在切换到ContentControl。
由于您正在使用UserControl,我假设您还必须切换到使用Generic.xaml模板。转换并不太难(对于大多数事情来说,这是一个更好的解决方案。)
8
2018-03-30 00:13
C#中的对象一旦不再使用就不会自动“释放”。
相反,当您从Control中删除对象时,它就会变为 有资格进行垃圾收集 此时,假设您没有对该UIelement的其他引用。
一旦对象被“无根”(没有直接或间接地从您的应用程序中的任何使用对象引用),它就有资格进行收集。然后,垃圾收集器将最终清理您的对象,但是当发生这种情况时,您(通常)无法控制。
只要相信它最终会得到清理。这是C#(和.NET一般)的美妙之处 - 管理和担心,这是为您处理的。
编辑:经过一些测试后,看起来Window保持对UIelement的引用,直到下一个布局传递。您可以通过添加对以下内容的调用来强制执行此操作:
this.UpdateLayout();
从画布中删除元素后的子项。这将导致对象可用于GC。
3
2018-03-29 22:43
答案:
更改
public class MyControl : UserControl
至
public class MyControl : ContentControl
并且它将说再见(在第二次移除控件之后。)我还通过使用验证了内存不泄漏
Debug.WriteLine("mem: " + GC.GetTotalMemory(true).ToString());
另外,请参阅 这个:
您通过清除grid.Children删除TestControl,但它不能立即符合垃圾回收的条件。它上面的几个异步操作正在等待,并且在这些操作完成之前不能进行GC操作(这些操作包括在渲染引擎中引发Unloaded事件和一些清理代码)。
我验证了如果等到这些操作完成(比如通过在ContextIdle优先级上安排Dispatcher操作),TestControl就有资格获得GC,而不管TextBlock上是否存在绑定。
UserControl必须具有不能快速清理的内部事件,或者它可能是VS2010 RC的错误。我通过connect报告这个,但是现在切换到ContentControl。
由于您正在使用UserControl,我假设您还必须切换到使用Generic.xaml模板。转换并不太难(对于大多数事情来说,这是一个更好的解决方案。)
8
2018-03-30 00:13
C#中的对象一旦不再使用就不会自动“释放”。
相反,当您从Control中删除对象时,它就会变为 有资格进行垃圾收集 此时,假设您没有对该UIelement的其他引用。
一旦对象被“无根”(没有直接或间接地从您的应用程序中的任何使用对象引用),它就有资格进行收集。然后,垃圾收集器将最终清理您的对象,但是当发生这种情况时,您(通常)无法控制。
只要相信它最终会得到清理。这是C#(和.NET一般)的美妙之处 - 管理和担心,这是为您处理的。
编辑:经过一些测试后,看起来Window保持对UIelement的引用,直到下一个布局传递。您可以通过添加对以下内容的调用来强制执行此操作:
this.UpdateLayout();
从画布中删除元素后的子项。这将导致对象可用于GC。
3
2018-03-29 22:43
在C#中有3代垃圾收集,因此即使没有对象的引用,也可能需要3个垃圾收集来释放它们。
您可以使用GC.Collect()的参数强制执行第3代垃圾回收,
但是,最好的approuch是不要自己调用GC.Collect(),
而是使用IDisposable接口并将Children绑定到ObservableCollection
当你得到任何被删除对象的CollectionChanged事件Dispose()时。
3
2018-03-30 00:21
你可能会发现这个有趣的。我最近发现x:Name标记扩展在由字符串名称键入的字典中存储父控件中UIElement的引用。
从其父项中删除UIElement时,字典将保留对该控件的引用。
这里有一篇博文/视频调试内存泄漏: WPF
x:名称内存泄漏
解决方案是不使用x:Name或确保清除x:Name保持活动的控件,以便在收集可视树的一部分之前不占用太多内存。
更新: 您使用。取消注册命名类 名称范围
this.grid.Children.Remove(child); // Remove the child from visual tree
NameScope.GetNameScope(this).UnregisterName("child"); // remove the keyed name
this.child = null; // null the field
// Finally it is free to be collected!
2
2018-06-02 09:51
我们有同样的问题,也认为这可能是原因。但我们使用Memory Profiler工具分析了我们的项目,发现与Grid.Remove或Grid.RemoveAt无关。所以我认为我的建议就是在内存分析器工具中查看你的项目,看看你的项目中发生了什么。希望这有帮助。
0
2018-03-30 04:13