以下XAML是否会泄漏内存?

时间:2010-08-23 22:13:13

标签: c# wpf

我正在使用.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根本没有清理它们......

3 个答案:

答案 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