2

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

笔记:

  • 我安装了 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 上完成的,存在某种错误......

试过:

  • 在没有运气GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);之后尝试调用SaveBitmapSource()
4

3 回答 3

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;
于 2014-09-25T17:36:36.477 回答
1

我已通过致电解决了问题

GC.WaitForPendingFinalizers();

紧随其后SaveBitmapSource()

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

于 2014-02-25T14:46:31.597 回答
0

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

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

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

于 2014-09-23T21:33:29.673 回答