调试C#中的一个主要数据绑定内存泄漏



我设置了以下数据绑定,这会导致巨大的内存泄漏:

<DataGrid x:Name="dataGridReflections" Width="auto" MaxWidth="1570" HorizontalContentAlignment="Center" AutoGenerateColumns="False" SelectionMode="Single" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" Margin="0,0,240,0">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Foreground" Value="{Binding ColourSet}"/>
<Setter Property="FontSize" Value="{Binding FontSize}"/>
<Setter Property="FontWeight" Value="{Binding FontWeight}"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontWeight"  Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="16"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Bearing (PLL)" Width="*" MaxWidth="221" Binding="{Binding BearingPLL, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn  Header="Bearing (°)" Width="*" MaxWidth="221" Binding="{Binding BearingDegrees, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn  Header="Subtended (PLL)" Width="*" MaxWidth="221" Binding="{Binding SubtendPLL, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn  Header="Subtended (°)" Width="*" MaxWidth="221" Binding="{Binding SubtendDegrees}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn  Header="Range" Width="*" MaxWidth="221" Binding="{Binding Range, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn  Header="Intensity" Width="*" MaxWidth="221" Binding="{Binding Intensity, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Time" Width="*" MaxWidth="221" Binding="{Binding Time, Mode=OneWay}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>

其在代码后面链接如下:

private void UpdateUserInterface()
{
if (ReflectionsReported != null)
ReflectionsReported.Clear();
ReflectionList reflectionDatas;
for (ReflectionCount = 0; ReflectionCount < SmallestArray(); ReflectionCount++)
{
reflectionDatas = new(ReflectionCount);
ReflectionsReported.Add(reflectionDatas);
}
labelMarkers.Content = Reflections.Revolution[(int)Reflections.Data.Marker][0];
labelReflections.Content = dataGridReflections.Items.Count;
}

反射列表定义为:

public ReflectionList(int i) : base()
{
ColourSet = ReflectionsUC.normal;
FontWeight = FontWeights.Normal;
ReflectionData reflr = new()
{
BearingPLL = Reflections.Revolution[(int)Reflections.Data.Bearing][i],
BearingDegrees = Math.Round((decimal)Convert.ToInt32(Reflections.Revolution[(int)Reflections.Data.Bearing][i]) / 65536 * 360, 3),
SubtendPLL = Reflections.Revolution[(int)Reflections.Data.Subtend][i],
SubtendDegrees = Math.Round((decimal)Convert.ToInt32(Reflections.Revolution[(int)Reflections.Data.Subtend][i]) / 65536 * 360, 3),
Intensity = Reflections.Revolution[(int)Reflections.Data.Intensity][i],
Range = Reflections.Revolution[(int)Reflections.Data.Range][i],
Time = DateTime.Parse(Reflections.Revolution[(int)Reflections.Data.Time][0]).ToString("HH:mm:ss")
};

每个值定义为:

public class ReflectionData : INotifyPropertyChanged
{
private string _bearingPLL;
private decimal _bearingDeg;
private string _subtendPLL;
private decimal _subtendDeg;
private string _intensity;
private string _range;
private string _time;
public string BearingPLL { get { return _bearingPLL; } set { if (_bearingPLL != value) { _bearingPLL = value; OnPropertyChanged(); } } }
public decimal BearingDegrees { get { return _bearingDeg; } set { if (_bearingDeg != value) { _bearingDeg = value; OnPropertyChanged(); } } }
public string SubtendPLL { get { return _subtendPLL; } set { if (_subtendPLL != value) { _subtendPLL = value; OnPropertyChanged(); } } }
public decimal SubtendDegrees { get { return _subtendDeg; } set { if (_subtendDeg != value) { _subtendDeg = value; OnPropertyChanged(); } } }
public string Intensity { get { return _intensity; } set { if (_intensity != value) { _intensity = value; OnPropertyChanged(); } } }
public string Range { get { return _range; } set { if (_range != value) { _range = value; OnPropertyChanged(); } } }
public string Time { get { return _time; } set { if (_time != value) { _time = value; OnPropertyChanged(); } } }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}

每次反射时都会触发更新用户界面方法。革命充满了新的数据。如果我删除所有用户界面元素,只输入数据,就不会出现内存泄漏,但一旦我允许运行更新用户界面方法,我一小时内就可以获得12 GB的内存!有没有一种更好/更简单的方法可以让我在处理/内存使用最少的情况下做到这一点,我知道我这里确实有很多数据,每0.125秒更新一次。

这个问题很愚蠢,但不是一个愚蠢的想法:您是否使用内存探查器运行了代码,以便查看是什么导致了泄漏?哪些课该走的时候还在附近?

当垃圾收集器由于对象仍在被引用而无法清理对象时,就会发生泄漏。如果没有内存探查器,我会查看任何创建新对象或附加事件处理程序的代码,因为这些代码最有可能卡在内存中。您有一个实现INotifyPropertyChanged接口的类,当绑定为.时,可能没有删除事件处理程序

如果你有一个工作样本,这将更容易诊断,当然这并不总是容易做到的,所以如果你没有访问内存探查器的权限,你可以做一些技巧,比如实现IDisposable或Finalizers,看看你的类是否被处理过。您可以在类中放置一个计时器,在类存在的每分钟将其哈希或构建时间打印到控制台。您还可以使用GC强制执行垃圾收集。Collect((

https://learn.microsoft.com/en-us/dotnet/api/system.gc.collect?view=net-5.0

我的建议是下载一个内存探查器并使用试用版。JetBrains有一个为期5天的全功能试用版。

最新更新