Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.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
当窗口有一个带有TabItem的TabControl(或延迟内存回收?)时,C#WPF内存泄漏_C#_Wpf_Memory Leaks_Tabcontrol_Tabitem - Fatal编程技术网

当窗口有一个带有TabItem的TabControl(或延迟内存回收?)时,C#WPF内存泄漏

当窗口有一个带有TabItem的TabControl(或延迟内存回收?)时,C#WPF内存泄漏,c#,wpf,memory-leaks,tabcontrol,tabitem,C#,Wpf,Memory Leaks,Tabcontrol,Tabitem,我在一个应用程序中遇到了一个非常明显的内存泄漏,该应用程序将打开一个设置窗口,其中包含一个包含许多TabItems的TabControl。起初,我认为其中一个显示的用户控件一定是罪魁祸首,于是我注释了一大堆东西,最后拿出JetBrains dotMemory并制作了一个演示程序 这个问题(我想) 当窗口包含至少包含一个TabItem的TabControl时,当窗口关闭时,窗口对象仍然存在。如果TabControl中没有TabItems,窗口对象将立即销毁(如预期的那样) 保留期 根据dotMem

我在一个应用程序中遇到了一个非常明显的内存泄漏,该应用程序将打开一个设置窗口,其中包含一个包含许多TabItems的TabControl。起初,我认为其中一个显示的用户控件一定是罪魁祸首,于是我注释了一大堆东西,最后拿出JetBrains dotMemory并制作了一个演示程序

这个问题(我想)

当窗口包含至少包含一个TabItem的TabControl时,当窗口关闭时,窗口对象仍然存在。如果TabControl中没有TabItems,窗口对象将立即销毁(如预期的那样)

保留期

根据dotMemory,“保留”来自WindowAutomationPeer(.\u所有者)、TabControlAutomationPeer(.\u父对象)、TabItemAutomationPeer(.\u父对象)、ElementProxy(.\u父对象),然后在底部显示“RefCounted handle”

复制

创建一个名为“TabsInWindows”的新C#WPF应用程序(目标框架:.NETFramework 4.7.2)

将按钮添加到主窗口:

<Window x:Class="TabsInWindows.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="300" Width="300">
    <Grid>
        <Button Content="Open tab window" Click="Button_Click"/>
    </Grid>
</Window>
启动应用程序。每次按下按钮时,都会创建一个新窗口,但TabItem(?)会在窗口关闭时保留,因此TabWindow也会保留。(重复任意次数)

如何修复?

我的问题是,在我的实际应用程序中,所有选项卡的所有内容似乎都保留在内存中,导致了严重的内存泄漏

在演示应用程序中,我尝试过做一些事情来避免挂起的物体;将网格的内容设置为null。清除选项卡控件(主题)中的项目。清除网格中的子元素。 所有这些都不起作用

我不知道什么是“AutomationPeer”-对象,或者ElementProxy是由什么创建的,以及为什么它不会消亡

如果有人能告诉我如何避开这个问题,或者能解释什么是ElementProxy以及它为什么会出现,这将是非常有帮助的

在写这篇文章时,我确实让dotMemory与测试应用程序一起运行,在最后做了一件事之后,这些对象似乎已经被删除了

这就引出了一个问题:在移除一个对象之前,我能期望它在内存中有多长时间是可见的,并且带有引用

在实际项目中

然后,我在实际项目中尝试了类似的方法,确保非我们自己的控件直接链接到SettingsWindow(我不排除我们的一个控件中存在内存/引用问题,因此直接在“键保留路径”中列出的任何控件都已注释掉)。 剩下的“3个唯一分支”,一个是来自ListBox自身扩展的“EffectiveValueEntry[40]”,另两个是“EffectiveValueEntry”([19]和[22]),都来自TextBlock、TextBlockAutomationPeer[4]、List、ListBoxItemAutomationPeer、ElementProxy

在大约十分钟的无所事事后,设置Swindow仍然存在,但“密钥保留路径”已更改,“20个唯一分支”都是有效的ValueEntry(第一个是[32],其余是[42])、TextBox、TextEditor,但现在“F-Reachable Queue”位于列表底部

又过了大约十分钟,布景终于消失了

然后,我又打开了设置窗口几次,在关闭最后一个窗口一分钟后,只有“文本框”(TextBox)引用离开了,随后强制垃圾收集(使用dotMemory中的按钮),对象引用消失了

该相信什么?

所以很明显,如果我等待足够长的时间,“魔法”就会发生——但这是一台电脑——不是魔法盒

有谁能告诉我为什么有些物体会在记忆中出现更长时间,但最终会被移除?我应该期望这些物体在我周围停留多久

我还想找到一种方法来防止这些“鬼”对象出现在TabItems中,如果它们最终会被删除,那么它们就没有理由占用内存。。。 你看,我在SettingsWindow中对一些UI组件进行性能测试时发现了这一点,并且随着内存的使用,重复测试花费的时间越来越长,因此简单地等待引用消失并不是一个很好的选择


如果你无能为力;感谢您抽出时间阅读我的文字墙…

设置选项卡窗口的所有者

private void Button_Click(object sender, RoutedEventArgs e)
{
       TabsWindow w = new TabsWindow();
       w.Owner = this;
       w.Show();
}

设置选项卡窗口的所有者

private void Button_Click(object sender, RoutedEventArgs e)
{
       TabsWindow w = new TabsWindow();
       w.Owner = this;
       w.Show();
}

您所看到的是,有证据表明该窗口没有及时清除其引用(资源),并且没有资格进行后续的第2代垃圾收集。GC应该在需要获取更多内存时收集您的窗口,正如您所说,该窗口稍后会被收集,这意味着它迟早会消失。@XAMlMAX有办法强制它清理吗?我知道这不是最好的解决方案,但您可以尝试调用
GC.Collect()完成后,但不是在关闭事件上。我不能保证这会有帮助,或者它会收集物品,但值得一试。垃圾收集的链接。@XAMlMAX我怀疑这会有什么不同,因为在界面上按那个按钮时,它几乎没有效果。我已经尝试过关闭和删除内容的所有选项卡,这似乎有助于更快地回收包含的某些对象。空的TabItem至少比包含对象的TabItem占用更少的内存。为什么您关心内存的回收速度?您看到的证据表明,该窗口没有及时清除其引用(资源),并且有资格进行后续的第2代垃圾回收。GC应该在需要获取更多内存时收集您的窗口,正如您所说,该窗口稍后会被收集,这意味着它迟早会消失。@XAMlMAX有办法强制它清理吗?也许我能做点什么
private void Button_Click(object sender, RoutedEventArgs e)
{
       TabsWindow w = new TabsWindow();
       w.Owner = this;
       w.Show();
}