我正在使用.NET4 WPF DataGrid来显示包含大量图像的SQL表。
有问题的XAML代码是:
...
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Converter={StaticResource ImageConverter}, Path=Picture}" Stretch="Uniform" MaxHeight="200" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
...
ImageConverter是这样编写的:
[ValueConversion(typeof(Binary), typeof(BitmapImage))]
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = new System.IO.MemoryStream((value as Binary).ToArray());
bi.EndInit();
if (bi.CanFreeze) bi.Freeze();
return bi;
}
else return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
我试图对此进行一些分析,但我不确定我是否正确地解释了结果。
首先,包含图像的SQL表使用38MB的磁盘空间(图像应以png格式存储)。通过LINQ将所有图像加载到数据网格后,该应用程序使用了大约170多MB的额外RAM。这可能有助于解压缩图像以及甚至在启用虚拟化时wpf数据网格是一个巨大的内存耗的事实。关闭窗口后,内存使用量不会下降。重新打开窗口会导致使用另外170 MB的RAM,使总内存使用量大约为400MB。如果我再次重新打开窗口,那么内存使用量将下降到大约330MB。再次重新打开窗口会占用内存,达到380MB。重新打开再次使其达到270MB。重新打开再次使其达到426MB。所以你可以看到非常波动......
使用Win7 x64和8GB RAM完成了一些测试(应用程序使用Any CPU选项进行编译)。
我在虚拟xp机器上尝试了相同的测试,其中有<512MB的ram(如果我没记错的话,大约是384)。每当我重新打开窗口时,我首先看到在加载窗口时内存使用率急剧下降,然后重复使用相同的内存,因此整体使用情况大致相同。
我解释这些结果,除非内存子系统存在高压,否则GC不会打扰清理。但是在那种情况下,关闭窗口后调用GC.Collect()会释放大部分内存吗?只有我没有,当我调用GC.Collect时,我看到只有一点4-6MB的内存使用量(我也尝试了所有可能的参数,包括强制收集所有代并调用GC.WaitForPendingFinalizers())。
这让我想到可能未使用的泄漏数据被推送到页面文件中。但是页面文件的使用率根据内存使用情况下降并上升。
所有事情都认为这里不应该有内存泄漏,但是在关闭窗口后我无法让内存使用率下降。我确实尝试将一些.NET分析器附加到我的进程,但是这些分析器非常复杂,我无法弄清楚所显示的图像对象是否仍然存在并被某些东西引用。或者他们已经死了,GC根本没有清理它们......
答案 0 :(得分:4)
您可以通过GC.GetTotalMemory(true)
了解内存是否写入页面文件的方式。这应该返回.NET进程的实际内存消耗,包括可能已写入页面文件的任何页面。
这可能与例如任务管理器报告的内存,但它将报告.NET对象实际分配的内容。
有关任务管理器和GetTotalMemory
报告的内存消耗差异的详细信息,请参阅http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/2ebbff38-f881-47ad-a4b4-9c9157fb3b5b。
我的猜测是GC.GetTotalMemory(true)
将报告收集后实际上已释放所有内存。
答案 1 :(得分:1)
在我看来,该代码中没有内存泄漏。事实上,虚拟机内存几乎立即下降意味着GC正在正常工作。
我发现在类似情况下有用的一件事就是在某个地方放置一个调用GC.Collect的按钮,并多次点击它。通常在约3次死亡物体被“GCollected”之后,如果不是这种情况,你可能会有泄漏。
答案 2 :(得分:0)
DataGridTemplateColumn的memoryleak问题似乎已经消失了.NET 4.5