Кнопка XAML не собирает мусор после удаления ссылок - PullRequest
8 голосов
/ 15 ноября 2011

Я написал тестовую программу, в которой один Button определен в XAML как содержимое Window.При загрузке окна Button программно заменяется как содержимое окна, и поле, ссылающееся на него, также заменяется, и другим Button, который я создал программно.После этого я отслеживаю оба объекта Button, используя слабые ссылки, и опрашиваю с интервалом 1/10 секунды свойство IsAlive каждого из них.Перед первой проверкой IsAlive при первом вызове метода опроса я также стираю корневые ссылки на программно определенный Button.

Ожидание при запуске этого кода будет таким, несмотря наиз-за недетерминированности времени сборки мусора в C # оба объекта Button в конечном итоге будут сообщаться как сборщики мусораХотя программно определенный Button демонстрирует это поведение, обычно в течение 1/2 минуты, XAML Button никогда не собирается.Я оставил запущенную программу более десяти минут, наблюдая это поведение.

Может кто-нибудь сказать мне, почему объект XAML Button не собирается? В частности, я хотел бы знать, где ссылка на блокировку сборки мусора , находится ли она в моем коде или в реализации WPF.Возможно, это объект загрузки XAML.Я смотрю на какую-то утечку памяти?

Программа, описанная выше, включена ниже для справки.

MainWindow.xaml:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="300" Height="150" Loaded="Window_Loaded">
    <Button Name="btn" />
</Window>

MainWindow.xaml.cs:

namespace Test {
    public partial class MainWindow : System.Windows.Window {

        private System.WeakReference wr_xamlBtn, wr_programmaticBtn;

        public MainWindow() {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e) {

            // Immediately upon the window's loading, create a weak reference to the
            //  button %btn defined in XAML.
            wr_xamlBtn = new System.WeakReference(btn);

            // Replace references in %btn and this.Content to the XAML button with
            //  references to a programmatically-defined button. This would be
            //  expected to free the XAML button for garbage collection.
            btn = new System.Windows.Controls.Button();
            Content = btn;

            // Create a weak reference to the programmatically-defined button, so that
            //  when (strong) references to it are removed, it will be eligible for
            //  garbage collection.
            wr_programmaticBtn = new System.WeakReference(btn);

            // Provides a polling mechanism to see whether either the XAML button or
            //  the programmatically-defined button has been collected.
            var dt = new System.Windows.Threading.DispatcherTimer();
            dt.Tick += Poll;
            dt.Interval = System.TimeSpan.FromMilliseconds(100);
            dt.Start();
        }

        void Poll(object sender, System.EventArgs e) {

            // If the programmatically-defined button hasn't had its references
            //  removed yet, this does so. This makes it eligible for garbage
            //  collection.
            if (btn != null)
                Content = btn = null;

            // Uses the console to show a timestamp and the state of collection of the
            //  XAML button and the programmatically-defined button.
            System.Console.WriteLine(
                string.Format(
                    "XAML button {0}, Programmatically-defined button {1} @ {2}",
                    wr_xamlBtn.IsAlive ? "Alive" : "Collected",
                    wr_programmaticBtn.IsAlive ? "Alive" : "Collected",
                    System.DateTimeOffset.Now));
        }
    }
}

App.xaml:

<Application x:Class="Test.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml" />

1 Ответ

2 голосов
/ 16 ноября 2011

Кнопка не собрана, поскольку на нее сильно ссылались в области имен окна:

MemProfiler snapshot

Но она не должна распознаваться как утечка памяти, потому что следует перерегистрировать ваша новая кнопка в области видимости:

//...
INameScope scope = NameScope.GetNameScope(this);
scope.UnregisterName("btn");
btn = new System.Windows.Controls.Button();
Content = btn;
scope.RegisterName("btn", btn);
//...
...