更新:我在另一台安装得更干净的机器上尝试过这一点。我无法在那台机器上重现这一点。如果我找到导致此问题的违规(VSStudio)组件,我会让您知道。

我从代码后面创建了一些 UIElements,并期望垃圾收集来清理东西。然而,这些对象并没有在我预期的时间被释放。我期望它们在RemoveAt(0)处被释放,但它们只在程序结束时被释放。

当从 Canvas 的 Children 集合中删除对象时,如何释放对象?

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

没有正确的解决方案

其他提示

更改

public class MyControl : UserControl

public class MyControl : ContentControl

和它会说再见(在第二时间之后删除的控制)。我也验证了存储器不通过使用

泄漏

Debug.WriteLine("mem: " + GC.GetTotalMemory(true).ToString());

此外,请参阅

  

您通过清除grid.Children删除系统testControl,但它不是垃圾收集立即资格。它几个异步操作挂起,并且它不能GC'd直至完全那些操作(这些包括提高了无载事件,并在渲染引擎一些清理代码)。

     

我核实,如果等到这些操作完成(通过安排在ContextIdle优先级调度操作说)中,成为系统testControl资格GC,独立于TextBlock的有约束力的存在。

用户控件必须具有不快速清理内部事件,也可能是用VS2010 RC的错误。我想通过连接该报告,但现在切换到ContentControl中。

由于您使用的用户控件,我想你还必须切换到使用Generic.xaml模板。这不是太困难一个转换(对于大多数的东西是一个更好的解决方案。)

在C#对象不会自动“释放”,只要它们不再使用。

相反,当你从你的控制中删除该对象,就变成的符合垃圾收集的在这一点上,假设你没有其他引用到的UIElement。

一旦对象是“无根”(没有参考文献,直接或间接地从任何使用的对象在应用程序中),它成为符合回收。垃圾收集器会那么,最终,清理你的对象,但是当发生这种情况是不是你(典型值)的控制。

只要相信它最终将得到清理。这是C#的美丽(和.NET一般) - 管理,和忧虑,在此为您处理


编辑:一些测试后,看来该窗口保持,直到下一个布局传递到的UIElement的引用。你可以强制通过增加一个呼叫到发生:

this.UpdateLayout();

除去从画布儿童元件(一个或多个)之后。这将导致对象为可用于GC

有3代的垃圾收集在C#中,所以即使有你的对象的引用,它可能需要3个垃圾收集来释放他们。

可以使用GC.Collect的()的参数,以强制第三代无用单元收集,结果, 然而,最好的approuch是不调用GC.Collect()自己,结果 改用IDisposable接口和绑定儿童到一个ObservableCollection 当你得到任何移除的对象的CollectionChanged事件的Dispose()。

您可能会对此感兴趣。我最近发现 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! 

我们有同样的问题,也认为这可能是原因。但是,我们分析了使用存储器剖析工具我们的项目,发现没有什么做的Grid.Remove或Grid.RemoveAt。因此,我认为我的建议只是看看你的内存分析器工具项目,看看有什么是你的项目中发生的事情。希望这有助于。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top