WPF DataGrid数据更新时出现内存泄漏问题求助
内存泄漏原因排查
- 1 未冻结的SolidColorBrush实例无法回收:你每秒刷新时都会新建3个SolidColorBrush对象,该类继承自Freezable,未冻结的Freezable对象会关联当前UI线程的Dispatcher,且WPF绑定系统会对这类动态依赖对象创建强引用,导致GC无法回收,内存持续上涨。
- 2 INotifyPropertyChanged未正确实现:Datos类虽然声明了PropertyChanged事件,但所有属性的setter都没有触发该事件,WPF针对未正确实现属性通知的绑定会生成强引用的绑定表达式,强持有Datos实例,无法被正常回收。
- 3 多余的属性通知逻辑:ObservableCollection本身自带集合变更通知,Clear、Add操作会自动通知UI更新,手动触发Hmis的PropertyChanged属于多余操作,会产生额外的绑定更新开销,加重内存负担。
- 4 额外优化点:DataGrid未开启UI虚拟化,每次集合变更都会生成新的行控件对象,也会增加不必要的内存占用。
可行解决方案
第一步 优化SolidColorBrush使用
优先预定义全局冻结的Brush实例,避免重复创建:
// 全局静态定义,只需创建一次 public static readonly Brush EstadoGreenBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF5DC75D")); // 静态构造函数中冻结Brush static MainWindow() { EstadoGreenBrush.Freeze(); }
如果需要动态生成颜色,每次创建完SolidColorBrush后立即调用Freeze()方法:
var brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF5DC75D")); brush.Freeze(); dato1.Estado = brush;
第二步 修正Datos类的属性通知实现
所有属性的setter都触发PropertyChanged事件:
public class Datos : INotifyPropertyChanged { private Brush _estado; public Brush Estado { get => _estado; set { _estado = value; OnPropertyChanged(nameof(Estado)); } } private string _dato1; public string Dato1 { get => _dato1; set { _dato1 = value; OnPropertyChanged(nameof(Dato1)); } } private string _dato2; public string Dato2 { get => _dato2; set { _dato2 = value; OnPropertyChanged(nameof(Dato2)); } } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
第三步 移除多余代码
删掉ActualizacionUI_Tick方法中手动触发Hmis属性变更的代码:
// 删除这一行 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Hmis"));
第四步 开启DataGrid UI虚拟化(可选优化)
给XAML中的DataGrid添加虚拟化相关属性,复用行控件减少内存开销:
<DataGrid ItemsSource="{Binding Hmis, Mode=OneWay}" AutoGenerateColumns="False" IsReadOnly="True" EnableRowVirtualization="True" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
第五步 释放Timer资源(可选优化)
重写窗口的OnClosed方法释放Timer,避免Timer持有窗口引用导致窗口无法回收:
protected override void OnClosed(EventArgs e) { base.OnClosed(e); timer?.Dispose(); }
内容的提问来源于stack exchange,提问作者sneik15
相关产品推荐
相关产品推荐

