Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 在WPF中:Children.Remove或Children.Clear不';自由物体_.net_Wpf_Memory Management_Garbage Collection - Fatal编程技术网

.net 在WPF中:Children.Remove或Children.Clear不';自由物体

.net 在WPF中:Children.Remove或Children.Clear不';自由物体,.net,wpf,memory-management,garbage-collection,.net,Wpf,Memory Management,Garbage Collection,更新:我在另一台安装更干净的机器上试过这个。我无法在那台机器上复制这个。如果我发现是什么(VSStudio)组件导致了这种情况,我会让您知道 我从代码隐藏中创建了一些UIElements,并期望通过垃圾收集来清理这些东西。但是,在我预期的时间内,这些对象并不是自由的。我希望它们在RemoveAt(0)时被释放,但它们只在程序结束时被释放 从画布的子集合中删除对象时,如何释放这些对象 <Window x:Class="WpfApplication1.MainWindow" xmlns="ht

更新:我在另一台安装更干净的机器上试过这个。我无法在那台机器上复制这个。如果我发现是什么(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>
C#中的对象在不再使用时不会自动“释放”

相反,当您从控件中删除该对象时,假定您没有对该元素的其他引用,则该对象在该点上有资格进行垃圾收集

一旦对象“无根”(应用程序中没有直接或间接来自任何已使用对象的引用),它就有资格被收集。垃圾收集器最终会清理您的对象,但当这种情况发生时,您(通常)无法控制

只要相信它最终会被清理干净。这就是C#(和.NET)的美妙之处——这方面的管理和担忧都由您来处理


编辑:在一些测试之后,窗口似乎保存了对UIelement的引用,直到下一个布局过程。您可以通过向以下对象添加调用来强制执行此操作:

this.UpdateLayout();
从画布子对象中删除元素后。这将导致对象可用于GC。

更改

公共类MyControl:UserControl

公共类MyControl:ContentControl

它会说再见(在你第二次移除控件之后)。我还通过使用验证了内存没有泄漏

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

另见:

您可以通过清除grid.Children来删除TestControl,但它不能立即进行垃圾收集。它上的几个异步操作处于挂起状态,在这些操作完成之前(包括引发卸载的事件和渲染引擎中的一些清理代码),无法对其执行GC

我验证了,如果您等待这些操作完成(例如,通过以ContextIdle优先级调度调度程序操作),TestControl将符合GC的条件,这与TextBlock上是否存在绑定无关

UserControl必须具有无法快速清除的内部事件,或者可能是VS2010 RC的错误。我会通过connect报告,但现在切换到ContentControl


由于您使用的是UserControl,我假设您还必须切换到使用Generic.xaml模板。转换并不太困难(对于大多数情况来说,这是一个更好的解决方案)。

在C#中有三代垃圾收集,因此即使没有对对象的引用,也可能需要三代垃圾收集来释放它们

您可以使用GC.Collect()的参数强制执行第三代垃圾收集,
但是,最好的方法是不要自己调用GC.Collect(),
而是使用IDisposable接口并将子对象绑定到ObservableCollection
当您获得任何已删除对象的CollectionChanged事件Dispose()时。

我们也遇到了同样的问题,并且认为这可能是原因所在。但我们使用内存探查器工具分析了我们的项目,发现它与Grid.Remove或Grid.RemoveAt无关。因此,我认为我的建议是,在内存分析器工具中查看一下您的项目,看看您的项目中发生了什么。希望这能有所帮助。

您可能会对此感兴趣。我最近发现,x:Name标记扩展将父控件中对UIElement的引用存储在由字符串名称键控的字典中

从其父元素中删除UIElement时,字典将保留对控件的引用

这里有一篇博客文章/视频调试内存泄漏:

解决方案是不使用x:Name,或者确保清除由x:Name保持活动状态的控件,以便在收集可视化树的某个部分之前不消耗太多内存

更新:使用

我认为,对于OP所看到的行为的解释,最直接的问题是

这是一个经过修改的程序,实际上,它的行为完全符合OP的预期

与原来的不同之处在于:

  • 调用
    UpdateLayout()
    ,以确保已实现从布局系统中删除控件的效果

  • 等待Dispatcher完成任何挂起的处理(否则,用户控件完成的确切时间可能会有所不同,并且可能发生在比预期更晚的GC调用中)

  • 进行第二次GC
    收集
    等待FullGCComplete
    以确保GC确实有机会完成其工作

我在调试和发布版本中得到的输出是:

Adding control
Removing control
Goodbye
Adding control
Removing control
Goodbye
Adding control
Removing control
Goodbye
...
我认为这确实证明了垃圾收集是按照它应该的方式工作的

关于OP在不同PC上看到的不同行为的注意事项——谁知道这是否可能是2010年的某个bug,但我怀疑只是不同PC有不同的负载,这影响了GC处理的某些方面


我在VS2019中使用.NET framework 4.7.2进行了测试。

我知道,但在本例中不这么认为。当我在MouseDown处理程序的开头添加GC.Collect()时,它应该会选择符合垃圾收集条件的控件。没有。我推测在Canvas对象内部仍然必须有一些对它的引用。@Bart:尝试添加GC.Collect
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! 
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (main.Children.Count == 0)
    {
        Console.WriteLine("Adding control");
        main.Children.Add(new MyControl() { Background = Brushes.Yellow, Width = 100, Height = 50 });
    }
    else
    {
        Console.WriteLine("Removing control");
        main.Children.RemoveAt(0);
    }

    UpdateLayout();
    Dispatcher.Invoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.ContextIdle, null);

    GC.Collect(); // This should pick up the control removed at a previous MouseDown
    GC.WaitForPendingFinalizers(); // Doesn't help either
    GC.Collect();
    GC.WaitForFullGCComplete();
}
Adding control
Removing control
Goodbye
Adding control
Removing control
Goodbye
Adding control
Removing control
Goodbye
...