自动检查
为了简化您的工作,dotMemory 会自动检查快照中最常见的内存问题类型。 如果您不知道从哪里开始,这些检查可能是分析快照的一个很好的起点。 要打开 检查 视图,请点击快照分析选项卡中的相应选项卡。 除了自动检查外,该视图还提供显示内存使用情况和托管堆段碎片的图表。
内存使用图表

该视图提供了两个显示快照中内存使用情况的图表:
自动检查

字符串重复
重复创建具有相同值的字符串而不是重用现有字符串会浪费内存。 dotMemory 检测到重复的字符串并显示浪费了多少内存。
分析对象
点击检查标题中的链接或双击列表中的特定对象集。
解决问题
如果具有相同值的字符串浪费了大量内存或生成了大量流量(例如,如果您的应用程序解析文本输入),请考虑实现 字符串驻留。
稀疏数组
稀疏数组是大部分填充零元素的数组。 从性能和内存使用的角度来看,稀疏数组效率低下。 dotMemory 会自动找到稀疏数组,并向您显示由于它们而丢失了多少内存(被零值占用)。
分析稀疏数组
点击检查标题中的链接或双击列表中的特定对象。
可终结对象
可终结对象是使用 Finalize() 方法释放非托管资源的对象。 使用此模式的问题首先在于,可终结对象的生命周期至少会延长一个 GC 周期;其次,执行 Finalize() 方法的终结线程运行是不可预测的。 如果您希望尽快回收已释放的资源,这可能会导致问题,并可能导致性能突然下降。 dotMemory 检测并显示所有排队等待终结的对象以及 自上次快照以来已终结的对象。
分析可终结对象
解决问题
为导致问题的类型实现
IDisposable接口,并通过其Dispose()方法释放所有非托管资源。 有关 dispose 模式的更多信息,请参阅 Microsoft Learn。
事件处理程序泄漏
当您将一个对象(称为监听器)订阅到另一个对象(称为源)的事件时,会发生此类泄漏。 例如: Timer1.Tick += OnTimer; 在订阅期间,源对象会获取对监听器对象事件处理程序的引用。 如果您删除了监听器,该引用将阻止其被垃圾回收。 dotMemory 会自动找到在事件处理程序中被引用但从未从相应事件中取消订阅的对象。
分析对象
点击检查标题中的链接或双击列表中的特定对象。
解决问题
当不再需要时,请将监听器从事件中取消订阅。 例如:
Timer1.Tick -= OnTimer;
WPF 绑定泄漏
破坏 WPF 数据绑定模式也可能导致内存泄漏。 在您对源对象的某个属性执行数据绑定后,绑定目标对象会开始监听属性更改通知。 如果该属性不是 DependencyProperty 对象,并且目标对象未实现 INotifyPropertyChanged 接口,则可能会在源对象及其引用的每个对象中发生内存泄漏。 dotMemory 检测到此类绑定模式违规,并向您显示可能导致此类泄漏的对象列表。
分析对象
点击检查标题中的链接或双击列表中的特定对象。
解决问题
使源对象实现
INotifyPropertyChanged接口,或在不再需要时使用ClearBinding方法移除绑定。
WPF 集合绑定泄漏
此泄漏类似于上述的 WPF 绑定泄漏。 如果绑定到一个未实现 INotifyCollectionChanged 接口的集合,WPF 会创建对此集合的强引用。 因此,它会在整个应用程序生命周期内保留在内存中。 dotMemory 检测并显示此类对象。
分析对象
点击检查标题中的链接或双击列表中的特定对象。
解决问题
使源集合实现
INotifyCollectionChanged接口。 另一种方法是使用ObservableCollection集合,因为它已经实现了INotifyCollectionChanged接口。
依赖项属性泄漏
此类泄漏的原因与事件处理程序泄漏相同。 垃圾回收器不会回收通过 AddValueChanged 方法订阅 DependencyProperty 更改的对象,直到使用 RemoveValueChanged 方法取消订阅它们。 dotMemory 检测并显示所有此类对象。
分析对象
点击检查标题中的链接或双击列表中的特定对象。
解决问题
当订阅对象的生命周期结束时,请使用
RemoveValueChanged方法取消订阅它。
x:Name WPF 泄漏
此类泄漏是由于以下 WPF 特性导致的:WPF 会为在 XAML 中声明并使用 x:Name 指令的 UI 元素创建一个强全局引用。 例如:
因此,如果您动态移除了以这种方式声明的元素,它仍会保留在内存中。
分析对象
点击检查标题中的链接或双击列表中的特定对象。
解决问题
一种移除泄漏的方法是改为在 C# 代码中声明 UI 元素,而不是在 XAML 中。 另一种方法是在要移除 UI 元素时调用父控件的
UnregisterName方法。 例如:this.UnregisterName("myControl1");
堆碎片
使用 堆碎片 图表评估托管堆段的碎片情况:
第 1 代和第 2 代堆段。
大对象堆(LOH)——用于存储大对象(85,000 字节及以上)的单独堆。 LOH 碎片可能是一个严重的问题。
固定对象堆——用于存储禁止在堆中移动的对象的单独堆。 您可以将 固定对象作为单独的对象集打开。
冻结对象堆(FOH)——用于存储不可变对象的单独堆。
点击图表标题将打开 按代分组视图,显示快照中所有对象。

堆段名称。
段中堆的数量。
图表上的每个条形代表一个特定的堆。
由 GC 定义的堆段总大小。
图表上条形的长度对应于段中某些堆的总大小。 请注意, 总计 大小可能略大于 已固定、 已取消固定 和 free 的总和: 总计 大小还包括用于对齐、填充和其他特定用途的内存块。
固定对象的总大小。
固定对象禁止在堆中移动。 通常,此类对象由某些非托管代码使用,或者可能是使用
已修正语句的结果。分配在堆段中的所有对象(不包括固定对象)的总大小。
堆段中空闲内存的总大小。
堆碎片 还允许您打开一个特定的对象集——固定对象。