【问题标题】:XAML Button not garbage collected after eliminating references消除引用后 XAML 按钮未被垃圾收集
【发布时间】:2011-12-29 20:03:56
【问题描述】:

我编写了一个测试程序,其中在 XAML 中将单个 Button 定义为 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" />

【问题讨论】:

  • 强制对所有代进行垃圾收集。这能解决问题吗?
  • Poll(.) 方法中调用System.GC.Collect(); 只会导致立即收集以编程方式定义的按钮。我使用添加的此方法调用运行代码五分钟,但未产生对 XAML 按钮的预期效果。

标签: c# wpf xaml memory-leaks garbage-collection


【解决方案1】:

该按钮未被收集,因为它在 Window 名称范围内被强引用:

但不应将其识别为内存泄漏,因为您应在范围内重新注册新按钮:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-02-11
    • 2021-02-16
    • 1970-01-01
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    相关资源
    最近更新 更多