【问题标题】:TiffBitmapEncoder, memory bug causing Insufficient Memory Exception in C#/WPFTiffBitmapEncoder,导致 C#/WPF 中内存不足异常的内存错误
【发布时间】:2014-03-26 09:54:43
【问题描述】:

我有一个 WPF 应用程序,我通过使用 TiffBitmapEncoder 将它们转换为 TIFF 图像来保存数百个 BitmapSource。但是,我有这种奇怪的内存消耗,它经常抛出 Insufficient Memory Exception。

注意:

  • 我安装了 8GB 内存。
  • 图像尺寸从 10x10 到 300x300 像素不等(非常小)

这是有效的代码:

static void SaveBitmapSource(BitmapSource bitmapSource)
{
    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    encoder.Compression = TiffCompressOption.Zip;
    BitmapFrame frame = BitmapFrame.Create(bitmapSource);

    encoder.Frames.Add(frame);

    using (MemoryStream ms = new MemoryStream())
    {
        encoder.Save(ms);
    }
}

这是我记忆中的截图:

现在,如果我克隆 BitmapSource(即使只克隆一次),那么我会得到这个巨大的内存分配,从而导致内存不足异常。

static BitmapSource source2 = null;
static void SaveBitmapSource(BitmapSource bitmapSource)
{
    if (source2 == null)
    {
        source2 = bitmapSource.Clone();
    }
    TiffBitmapEncoder encoder = new TiffBitmapEncoder();
    encoder.Compression = TiffCompressOption.Zip;
    BitmapFrame frame = BitmapFrame.Create(source2);

    encoder.Frames.Add(frame);

    using (MemoryStream ms = new MemoryStream())
    {
        encoder.Save(ms);
    }
}

这是我对第二个代码示例的记忆截图

有谁知道这可能是什么原因以及如何解决?

不同之处在于第一个示例中的 BitmapSource 已渲染到屏幕上,而第二个示例中没有。我的怀疑是这可能与 GPU 和调度程序有关,可能是硬件加速转换,而第二个是在存在某种错误的 CPU 上完成的......

试过了:

  • 尝试在SaveBitmapSource() 之后调用GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);,但没有成功

【问题讨论】:

  • source2有什么处理方法吗?
  • BitmapSource 没有实现 IDisposable
  • 很好奇如果在函数末尾添加GC.Collect() 会影响内存使用。不是作为解决方案,而是作为诊断步骤。
  • 我试过 GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);但没有任何区别
  • 这是一个尖峰,而不是一个台阶。所以当然这不是内存泄漏。也与 GC 没有任何关系,这都是非托管代码。阻止您使用所有出色硬件的传统错误是强制您的程序以 32 位模式运行。您可以使用项目 + 属性、构建选项卡、平台目标设置来修复它。

标签: c# wpf memory out-of-memory bitmapencoder


【解决方案1】:

我已经通过调用解决了问题

GC.WaitForPendingFinalizers();

就在SaveBitmapSource()之后。

所以我的猜测是 BitmapSource 和/或 BitmapEncoder 内部有一些非托管资源,直到运行 Finalize 方法才被释放......

【讨论】:

    【解决方案2】:

    我很高兴这解决了这个问题。但我不相信这是正确的解决方法。 我看到您只使用一个参数调用 BitmapFrame.Create() 。您可能需要更仔细地查看..

    尝试使用 BitmapCacheOption.None 标志 - 默认情况下,它可能会无缘无故地将每个位图缓存在内存中:

    BitmapFrame.Create(source, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);

    【讨论】:

      【解决方案3】:

      你必须使用文件流来减少内存使用;

              BitmapDecoder decoder;
              using (Stream appendToOutput = File.Open(files[0], FileMode.Open, FileAccess.Read, FileShare.Read))
              {
                  decoder = BitmapDecoder.Create(appendToOutput, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                  using (Stream output = File.Open(outputFile, FileMode.Create, FileAccess.Write))
                  {
                      TiffBitmapEncoder childEncoder = new TiffBitmapEncoder();
                      if(Path.GetExtension(files[0]).Replace(".", "") == ScanningImageFormat.Jpeg) {
                          childEncoder.Compression = TiffCompressOption.Zip;
                      } else {
                          childEncoder.Compression = TiffCompressOption.Ccitt4;
                      }
      
                      foreach (BitmapFrame frm in decoder.Frames)
                      {
                          childEncoder.Frames.Add(frm);
                      }
      
                      List<Stream> imageStreams = new List<Stream>();
                      try
                      {
                          for (int i = 1; i < files.Count; i++)
                          {
                              string sFile = files[i];
                              BitmapFrame bmp = null;
                              Stream original = File.Open(sFile, FileMode.Open, FileAccess.Read);
                              imageStreams.Add(original);
                              bmp = BitmapFrame.Create(original, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                              childEncoder.Frames.Add(bmp);
                          }
                          childEncoder.Save(output);
                      }
                      finally
                      {
                          try
                          {
                              foreach (Stream s in imageStreams)
                              {
                                  s.Close();
                              }
                          }
                          catch { }
                      }
                  }
              }
              decoder = null;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-15
        • 1970-01-01
        • 2014-06-07
        • 2011-05-26
        • 2013-06-22
        • 2013-12-09
        • 1970-01-01
        相关资源
        最近更新 更多