gpt4 book ai didi

c# - GC 的固定实例 - 无法从我的托管代码中追踪

转载 作者:太空狗 更新时间:2023-10-29 17:57:31 24 4
gpt4 key购买 nike

所以我使用 WPF 3.5 和 MVVM + DataTemplate 方法在 GUI 上加载 2 个 View 。我在内存分析时观察到,作为项目控件的项目容器的一部分生成的项目被固定到内存中,即使在卸载 View 后也不会被 GC 处理!

我刚刚进行了测试,发现即使是最简单的代码也可以重现......你们可以自己检查一下。

XAML:

<Window x:Class="ContentControlVMTest.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ContentControlVMTest"
Title="Window2" Height="300" Width="300">
<DockPanel LastChildFill="True">

<CheckBox Click="CheckBox_Click" Content="Test1?"
DockPanel.Dock="Top" Margin="5"/>

<ContentControl x:Name="contentControl">
<ContentControl.Resources>

<DataTemplate DataType="{x:Type local:Test3}">
<TextBlock Text="{Binding C}" Margin="5"/>
</DataTemplate>

<DataTemplate DataType="{x:Type local:Test1}">
<DockPanel LastChildFill="True" Margin="5">
<TextBlock Text="{Binding A}"
DockPanel.Dock="Top"
Margin="5"/>
<ListBox ItemsSource="{Binding Bs}"
DisplayMemberPath="B"
Margin="5"/>
</DockPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>

代码隐藏:

public class Test3
{
public string C { get; set; }
}

public class Test2
{
public string B { get; set; }
}

public class Test1
{
public string A { get; set; }

private List<Test2> _Bs;
public List<Test2> Bs
{
get
{
return _Bs;
}

set
{
_Bs = value;
}
}
}

public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
this.KeyDown += Window_KeyDown;
}

private void Window_KeyDown
(object sender, System.Windows.Input.KeyEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl))
if (Keyboard.IsKeyDown(Key.LeftShift))
if (Keyboard.IsKeyDown(Key.LeftAlt))
if (Keyboard.IsKeyDown(Key.G))
{
GC.Collect(2, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(3, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(3, GCCollectionMode.Forced);
}
}

private void CheckBox_Click(object sender, RoutedEventArgs e)
{
if (((CheckBox)sender).IsChecked.GetValueOrDefault(false))
{
var x = new Test1() { A = "Test1 A" };
x.Bs = new List<Test2>();
for (int i = 1; i < 10000; i++ )
{
x.Bs.Add(new Test2() { B = "Test1 B " + i });
}
contentControl.Content = x;
}
else
{
contentControl.Content = new Test3() { C = "Test3 C" };
}
}
}

我通过 Left Shift + Alt + Ctrl + G 执行强制 GC。Test1Test3 View 和 View 模型的所有项目在正确卸载后都死掉了。所以这符合预期。

但在 Test1 模型(具有 Test2 对象)中生成的集合仍然固定在内存中。它表明该数组是列表框的项目容器使用的数组,因为它显示了列表框中的去虚拟化项目的数量!当我们在 Test1 View 模式下最小化或恢复 View 时,这个固定数组会改变它的大小!一次是 16 个项目,下一次是 69 个项目。

enter image description here

这意味着 WPF 执行项目控件中生成的项目的固定!谁能解释一下?这有什么明显的缺点吗?

非常感谢。

最佳答案

该问题是由于绑定(bind)机制未能完全释放实际已绑定(bind)显示在屏幕上的列表项引起的。最后一点几乎可以肯定是为什么您在不同的运行中看到不同数量的“孤立”实例。滚动列表越多,产生的问题就越多。

这似乎与 a bug report that I submitted over a year ago 中描述的同一类潜在问题有关因为固定根和固定实例树是相似的。 (要以方便的格式查看此类详细信息,您可能需要获取一份更高级的内存分析器的副本,例如 ANTS Memory Profiler。)

真正的坏消息是,您的孤立实例在窗口本身消亡后被固定,因此如果没有我在 WinForms 场景中必须使用的相同类型的 hack 来强制清理,您可能无法清理它们-结合私有(private)。

所有这一切中唯一的好消息是,如果您可以避免绑定(bind)到嵌套属性,那么问题就不会发生。例如,如果您将 ToString() 重写添加到 Test2 以返回其 B 属性的值并从您的列表框项目中删除 DisplayMemberPath,问题就会消失。例如:

public class Test2
{
public string B { get; set; }

public override string ToString()
{
return this.B;
}
}

<ListBox ItemsSource="{Binding Bs}" 
Margin="5"/>

关于c# - GC 的固定实例 - 无法从我的托管代码中追踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9378183/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com