【问题标题】:Memory 'leak' with MemoryStream and BitmapCacheOption.OnLoadMemoryStream 和 BitmapCacheOption.OnLoad 的内存“泄漏”
【发布时间】:2021-07-15 07:16:22
【问题描述】:

我必须从图像中获取 BitmapSource,为此我使用如下扩展方法:

public static BitmapSource ToBitmapSource(this Image image)
{
    LogEx.FunctionEnter();

    if (image == null)
        throw new ArgumentNullException("image");

    BitmapImage bitmapImage = null;
    using (MemoryStream memory = new MemoryStream())
    {
        image.Save(memory, ImageFormat.Jpeg);
        memory.Position = 0;
        bitmapImage = new BitmapImage();
        bitmapImage.BeginInit();
        bitmapImage.StreamSource = memory;
        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
        bitmapImage.EndInit();
    }

    LogEx.FunctionLeave("Rendered image as BitmapSource");
    return bitmapImage;
}

如果我现在释放它的句柄并处理原始图像,它会保留在内存中,即使在多次手动调用 GC 之后也是如此。我用这段代码测试了使用文件而不是流:

string filename = $"c:\\temp\\{page}.jpg";
if (File.Exists(filename))
{
    File.Delete(filename);
}
_highResPageImages[page].Save(filename, ImageFormat.Jpeg);
Uri uri = new Uri(filename);
BitmapImage source = new BitmapImage();
source.BeginInit();
source.UriSource = uri;
source.CacheOption = BitmapCacheOption.OnLoad;
source.EndInit();
Document.PageImage = source;
// Ducument.PageImage = _highResPageImages[page].ToBitmapSource();

即使还有一个 OnLoad,它也会在释放句柄时被释放。所以它一定是 MemoryStream 的东西。但是什么?我尝试了在其他地方找到的 WrapperStream,并使用了 BitmapImage 的 Freeze() 方法,但都无济于事。问题是,我无法将图像缓存在客户的驱动器上(即使这样做不会花费大量时间)并且我从另一个 DLL 获取图像,所以我无法事先更改它。别人有想法吗?

编辑:我使用 WPF 并且句柄的值正在用于显示的绑定中。也许这在某种程度上很重要。或者我在 Binding 和句柄中使用 BitmapSource 而不是原始 BitmapImage。

【问题讨论】:

  • 你怎么知道它会留在内存中?
  • 我有两种这样的方法。一种用于高分辨率图像,一种用于低分辨率缩略图。它们并行运行,然后我用 VS 对其进行分析,在我更改它之前说,我在高分辨率中使用了近 65MB 的 3 页,在低分辨率中使用了 19MB,在我更改之后,它说 0 MB 用于高分辨率,19 MB 用于低分辨率。在我发布到这里之前,我几乎整整一天都对其进行了分析。相信我;)
  • BitmapCacheOption.OnLoad 的全部意义在于它缓存在内存中。所以我不完全理解。
  • 对,但是当您释放句柄时(BitmapImage myImage = null;),即使使用 OnLoad,GC 也会收集 BitmapImage,将其处理并释放内存。但显然当您使用 MemoryStream 而不是文件句柄时它不会。
  • 谢谢,我现在明白了。听起来像一个错误。但我不知道他们是否会修复它,因为这是相当古老的东西。目前无法对此进行详细调查。也许你可以在the sources 中找到一些静态属性。编辑:但是,也可能是创建了图像的第二个引用/副本,这将阻止 gc 清理。

标签: c# memorystream bitmapimage bitmapsource


【解决方案1】:

ToBitmapSource试试这个:

...
bitmapImage.StreamSource = null;
return bitmapImage

【讨论】:

    猜你喜欢
    • 2012-03-09
    • 1970-01-01
    • 2010-12-02
    • 2013-11-08
    • 2016-05-03
    • 2011-11-25
    • 2010-11-19
    • 2014-09-27
    • 2013-07-31
    相关资源
    最近更新 更多