WPF视图模型未释放引发内存泄漏,如何解决Dispatcher相关问题?
WPF ViewModel内存泄漏解决:Dispatcher Lambda捕获this问题
问题根源
你的代码中,Dispatcher.CurrentDispatcher.Invoke的匿名Lambda捕获了ViewModel的强引用this。如果这个Lambda被Dispatcher队列持有(比如调用线程是Timer后台线程,Dispatcher需要排队执行委托),ViewModel会因为这个强引用无法被GC回收,最终导致内存泄漏。
解决方案
1. 使用弱引用(WeakReference)避免强引用捕获
通过创建ViewModel的弱引用,让Lambda只持有弱引用,不会阻止GC回收ViewModel。当ViewModel已被释放时,跳过更新操作:
public async Task RefreshAsync2(CancellationToken token = default) { var data = await Mediator.Send(new GetDataQuery()); if (data == null) return; // 持有ViewModel的弱引用,替代直接捕获this var weakViewModel = new WeakReference<YourViewModelType>(this); Dispatcher.CurrentDispatcher.Invoke(() => { if (weakViewModel.TryGetTarget(out var viewModel)) { viewModel.ViewModelInfo = data; } }); }
2. 从源头切断无效调用(结合IDisposable)
如果RefreshAsync2是由Timer等定时任务触发,在ViewModel销毁时停止定时任务,避免后续的Dispatcher调用:
public class YourViewModelType : INotifyPropertyChanged, IDisposable { private Timer _refreshTimer; public YourViewModelType() { // 初始化定时触发的Timer _refreshTimer = new Timer(_ => RefreshAsync2().ConfigureAwait(false), null, TimeSpan.Zero, TimeSpan.FromMinutes(1)); } public void Dispose() { // 销毁时停止Timer,避免后续调用RefreshAsync2 _refreshTimer?.Dispose(); _refreshTimer = null; } // 你的RefreshAsync2方法... }
3. 优化线程上下文判断(减少Dispatcher调用)
先检查当前线程是否拥有Dispatcher访问权限,如果是则直接更新字段,否则再用弱引用+Dispatcher:
public async Task RefreshAsync2(CancellationToken token = default) { var data = await Mediator.Send(new GetDataQuery()); if (data == null) return; if (Dispatcher.CurrentDispatcher.CheckAccess()) { // 当前在UI线程,直接更新 ViewModelInfo = data; } else { var weakViewModel = new WeakReference<YourViewModelType>(this); Dispatcher.CurrentDispatcher.Invoke(() => { if (weakViewModel.TryGetTarget(out var viewModel)) { viewModel.ViewModelInfo = data; } }); } }
补充说明
- 弱引用的核心是:只有当ViewModel还存活时,才执行更新操作,既解决了内存泄漏,也避免了对已释放对象的无效操作。
- 实现
IDisposable是后台任务场景下的常规操作,能从源头减少无效的异步调用,和弱引用配合使用效果最佳。
内容的提问来源于stack exchange,提问作者Luka
相关产品推荐
相关产品推荐

