You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF关闭窗口后定时器未停止、内存占用未降低问题咨询

问题根源

1. 内存无法释放的原因

MoviePosters类中定义的watchingMovie类型变量MW是静态变量,静态变量的生命周期和整个应用程序进程一致,只要你每次打开新的watchingMovie窗口时给MW赋值,旧的watchingMovie实例就会一直被这个静态变量持有强引用,哪怕你调用了Close()方法关闭窗口,GC也无法回收这部分实例占用的内存,所以每打开一次窗口内存就会持续上涨。

2. 窗口关闭后定时器仍运行的原因

有两个原因共同导致这个问题:

  • 你的清理逻辑只写在了自定义的Exit_Click点击事件中,如果用户通过点击窗口右上角的关闭按钮、Alt+F4等其他方式关闭窗口,这个事件不会触发,定时器也就不会被停止。
  • DispatcherTimer的执行依赖当前应用程序的UI线程调度队列,只要定时器没有被显式停止、Tick事件绑定没有被解除,哪怕对应的窗口已经关闭,只要UI线程还在运行,Tick事件就会持续被触发。
  • 额外注意:你当前Exit_Click方法内的执行顺序有误,先调用了GC回收,之后才停止定时器,执行GC的时候定时器仍处于运行状态,实例还被事件引用,自然不会被回收。

修复方案

  1. 移除静态变量引用
    删除MoviePosters类中的static watchingMovie MW;定义,如果确实需要持有当前打开的播放窗口引用,改为使用普通实例变量,并且在窗口关闭后置为null。
    修改后的MoviePosters代码示例:
namespace WpfApplication49
{
    public partial class MoviePosters : Window
    {
        // 去掉static修饰符
        private watchingMovie MW;

        private void myControl1_Click(object sender, myClickEventArgs e)
        {
            // 如果要限制同时只打开一个播放窗口,先判断已有实例是否存在,存在就先关闭
            if (MW != null)
            {
                MW.Close();
            }
            MW = new watchingMovie((string)e.my.Tag.ToString());
            // 订阅播放窗口的关闭事件,关闭后清空引用
            MW.Closed += (s, args) => MW = null;
            MW.Show();
        }
    }
}
  1. 统一窗口关闭清理逻辑
    不要把清理逻辑只放在按钮点击事件中,重写watchingMovie窗口的OnClosed方法,确保无论以什么方式关闭窗口,都会执行清理操作,同时修正执行顺序:
namespace WpfApplication49
{
    public partial class watchingMovie : Window
    {
        public readonly DispatcherTimer timer = new DispatcherTimer();

        public watchingMovie(String movie)
        {
            InitializeComponent();
            passedMovie = movie;
            timer.Interval = TimeSpan.FromMilliseconds(200);
            timer.Tick += timer_Tick;
            this.Topmost = true;
        }

        // 重写关闭方法,统一处理清理逻辑
        protected override void OnClosed(EventArgs e)
        {
            // 先清理定时器
            if (timer != null)
            {
                timer.Stop();
                timer.Tick -= timer_Tick;
            }
            // 清理媒体播放器资源
            if (ME != null)
            {
                ME.Stop();
                ME.Close();
                ME.Source = null;
            }
            base.OnClosed(e);
        }

        public void Exit_Click(object sender, RoutedEventArgs e)
        {
            // 直接调用Close即可,所有清理逻辑都在OnClosed里统一处理
            this.Close();
        }

        // 其余原有代码保持不变
        void timer_Tick(object sender, EventArgs e)
        {
            // 原有逻辑不变
        }
    }
}
  1. 删除手动GC调用
    不需要手动调用GC.Collect()等方法,只要所有强引用都被正确释放,GC会自动在合适的时机回收内存,手动调用GC不仅会降低程序性能,也无法解决因为强引用未释放导致的内存泄漏问题。

按照上面的方案修改后,关闭watchingMovie窗口时所有资源都会被正确释放,内存会回落至打开播放窗口前的水平,定时器也不会再继续运行。


内容的提问来源于stack exchange,提问作者StealthRT

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 08:54:01