为什么System.Drawing.dll中的C#OutOfMemory异常?

时间:2017-03-02 11:21:14

标签: c# .net winforms

让我们简化模型。

class Container
{
    //other members
    public byte[] PNG;
}

class Producer
{
    public byte[] Produce(byte[] ImageOutside)
    {
        using (MemoryStream bmpStream = new MemoryStream(ImageOutside), 
            pngStream = new MemoryStream())
        {
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(bmpStream);
            bitmap.Save(pngStream, System.Drawing.Imaging.ImageFormat.Png);
            pngStream.Seek(0, System.IO.SeekOrigin.Begin);
            byte[] PNG = new byte[pngStream.Length];
            pngStream.Read(PNG, 0, (int)pngStream.Length);
            bitmap.Dispose();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            return PNG;
        }
    }
}

主要功能是让Container container = new Container();为container.PNG和Queue.Enqueue(container)生成PNG 使用using()子句根本不起作用。

虽然这种情况重复了大约40次(因此而异),但它会引发异常。有时它是OutOfMemoryException,有时它就像“GDI +正常错误”(我不确定它是如何完全用英语,我只是翻译它)。 但是,如果我尝试捕捉异常而忽略它,它仍然可以继续产生更多但不是无限制的,只是更多的前进。 任务管理器中显示的占用内存仅在抛出第一个异常时大约为600 - 700 MB,最终停止在大约1.2 GB。我试过这个:

while (true)
{
    Bitmap b = new Bitmap(4500, 5000);
    list.Add(b);
    Invoke((MethodInvoker)delegate { textBox1.Text = list.Count.ToString(); });
}

虽然已经为程序分配了99%的内存(大约11GB),但它永远不会抛出任何异常,而且所有发生的事情都是textBox1中不再引发的数字。

避免这种情况的方法可能不是产生这么多东西,但我仍然想知道内部原则和理由,并感谢你的帮助。

1 个答案:

答案 0 :(得分:0)

使用byte[] PNG = new byte[pngStream.Length];

分配大部分内存来存储图像。

跟随调用它没用,你已经处理好了流。

GC.Collect();
GC.WaitForPendingFinalizers();

PNG数组使用的内存无法释放,因为函数返回中存在活动引用。

我建议返回一个流而不是一个字节数组。

否则,在您调用方法Produce之后,请记得在再次调用之前删除对PNG的引用。

样品:

while (true)
{
    Byte[] b = new Byte[1000];
    b = this.Produce(b);
    //Use your array as you need, but you can't assign it to external property, otherwise memory cannot be released 
    b = null; //remove the reference, (in reality, in this example assign null is not necessary, because b will be overwritten at next loop.
    GC.Collect(); //Force garbage collector, probably not necessarry, but can be useful
    GC.WaitForPendingFinalizers();
}

平台编译会影响最大可用内存:

  • 在32位应用程序中,最多有2 GiB的可用内存
  • 在64位应用程序中,您有2个可用内存的Tib,但是 单个对象(类)不能超过2个Gib。
  • 在UWP应用程序中,还有其他限制依赖于 设备
  • 启动应用程序时,及时编译任何CPU, 并且可以运行32位和64位,这取决于机器架构 和系统配置。