TiffBitmapEncoder,在C#/ WPF中导致内存不足的内存错误

时间:2014-02-24 16:05:59

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

我有一个WPF应用程序,我通过使用TiffBitmapEncoder将它们转换为TIFF图像来保存数百个BitmapSource。但是,我有这种奇怪的内存消耗,往往会导致内存不足。

注意:

  • 我安装了8GB的ram。
  • 图像尺寸从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);
    }
}

这是我记忆的屏幕截图:

No memory exception

现在,如果我克隆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);
    }
}

以下是第二个代码示例的内存屏幕截图

Memory exception

有谁知道造成这种情况的原因以及如何解决这个问题?

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

尝试:

  • GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);没有运气
  • 之后尝试拨打SaveBitmapSource()

3 个答案:

答案 0 :(得分:2)

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

        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;

答案 1 :(得分:1)

我通过调用

解决了这个问题
GC.WaitForPendingFinalizers();

SaveBitmapSource()之后。

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

答案 2 :(得分:0)

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

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

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