Pinned Instances for GC - Not traceable from my managed code

Итак, я использую WPF 3.5 с MVVM + метод DataTemplate для загрузки 2 представлений на GUI. При профилировании памяти я заметил, что элементы, сгенерированные как часть контейнера элементов управления items, сохраняются в памяти и не получают GCed даже после выгрузки представления!

Я только что провел тесты и обнаружил, что это воспроизводимо даже для самого простого кода... Вы, ребята, можете проверить сами.

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>

Code Behind:

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

Я выполняю принудительный GC с помощью Left Shift + Alt + Ctrl + G. Все элементы для Test1 или Test3 представления и View Model становятся мертвыми после их корректной выгрузки. Так что это соответствует ожиданиям.

Но коллекция, созданная в модели Test1 (которая имеет объекты Test2), остается зажатой в памяти. И это указывает на то, что массив используется контейнером items listbox, потому что он показывает количество девиртуализированных элементов из listbox! Этот прикрепленный массив меняет свой размер, когда мы сворачиваем или восстанавливаем представление в режиме просмотра Test1! Один раз это было 16 элементов, а в другой раз - 69 элементов при профилировании.

enter image description here

Это означает, что WPF выполняет пиннинг элементов, созданных в элементах управления! Кто-нибудь может объяснить это? Есть ли у этого какой-нибудь существенный недостаток?

Большое спасибо.

9
задан akjoshi 24 February 2012 в 11:39
поделиться