【问题标题】:out of memory exception when use control.BackgroundImage = Image.FromStream(memStream);使用 control.BackgroundImage = Image.FromStream(memStream); 时出现内存不足异常
【发布时间】:2011-12-08 05:58:51
【问题描述】:

我编写了一个从文件中读取 png 图像并通过控制显示的代码。
我想从流中读取图像并设置

control.BackgroundImage = Image.FromStream(memStream);

但是在使用这段代码时,会出现"out of memory"异常。但是在使用时

control.Image = Image.FromStream(memStream);

control.BackgroundImage = Image.FromFile(fileSource);

,这是工作。

图像文件大小为 5KB。

 if (System.IO.File.Exists(imgSource))
 {
  using (FileStream localFileStream = new FileStream(imgSource, FileMode.Open))
  {
  using (MemoryStream memStream = new MemoryStream())
  {
   int bytesRead;
   byte[] buffer = new byte[1024];

   while ((bytesRead = localFileStream.Read(buffer, 0, buffer.Length)) > 0)
   {
      memStream.Write(buffer, 0, bytesRead);
   }
   retIMG = Image.FromStream(memStream);

   pictureBox1.Image = retIMG;      // is work
   label1.Image = retIMG;       // is work
   button1.Image = retIMG;      // is work
   button1.BackgroundImage = retIMG;    // don't work
   groupBox1.BackgroundImage = retIMG;  // don't work
   panel1.BackgroundImage = retIMG; // don't work
  }
  }
 }

我认为是 .net 框架中的一个错误。 请你帮帮我?

【问题讨论】:

  • 哪一部分有问题?我建议您先将图像放入变量中,然后在第二次操作中赋值。图片有多大? 5ggb 文件大小可以是 50.000x50.000 像素 ;)
  • 图像报告“内存不足”,几乎所有出现问题。
  • 就像 Henk 所说的那样,GDI+(以及 System.Drawing)在许多情况下会抛出 OutOfMemory 错误,而实际上并不是内存不足,例如无效参数。我猜这是其中之一。

标签: c# winforms image exception


【解决方案1】:

阅读Image.FromStreamMSDN上的备注:

您必须在图像的生命周期内保持流打开。

因此,如果您在创建 MemoryStream 时删除 using,您的代码就可以正常工作。

当然,一旦您不再需要您创建的Image,您最好丢弃MemoryStream,尽管在这种情况下不调用Dispose() 并将其留给GC 收集它可能没有什么害处一次没用过。

它似乎适用于您的某些代码这一事实可能纯属运气,不应被视为可行的解决方案。请务必阅读文档以了解此类怪癖。

【讨论】:

    【解决方案2】:

    提供一些背景知识以添加到 DeCaf 的正确答案中。 GDI+ 非常努力地避免复制位图的像素。这很昂贵,占用几十兆字节的位图并不罕见。当您使用 Bitmap 构造函数或 Image.FromFile() 从文件加载位图时,GDI+ 会创建一个内存映射文件。仅在需要时才按需调入像素。非常有效,但它会锁定文件。显然,您试图避免在此代码中锁定。

    您确实可以通过自己使用 MemoryStream 将字节加载到内存中来避免这种锁定。但同样的原则仍然适用,GDI+ 仍然不复制像素,只在需要时从流中读取。当您 Dispose() 流时,这会出错。很难诊断,因为异常发生稍后,通常是在需要绘制位图时。它在绘画代码中爆炸,除了 Application.Run() 之外,您没有任何代码可以查看。带有蹩脚的异常消息,GDI+ 只有少数错误代码。您没有内存不足,它只在 GDI+ 中看起来那样,否则它无法弄清楚为什么流突然不再可读。

    至少部分问题是由 MemoryStream.Dispose() 的非常笨拙的实现引起的。 Dispose 旨在释放 非托管 资源。内存流没有任何内容,它只拥有内存。这已经被垃圾收集器处理了。不幸的是,他们还是实施了它。不是通过实际处置任何东西,因为没有什么可处置的,而是通过将 MemoryStream 标记为不可读。当它在绘制位图时尝试读取时会触发 GDI+ 中的错误。

    因此,只需删除 using 语句即可避免处理 MemoryStream 来解决您的问题。当不再使用位图时,不要担心稍后将其处理掉。没什么可处理的,垃圾回收器会自动释放内存。

    【讨论】:

    • 实际上MemoryStream 可能以WaitHandle 的形式保存非托管资源,即如果在其上调用了BeginReadBeginWrite。在这里不太可能成为问题,但我想我还是应该指出来。
    【解决方案3】:

    这两件事共同解决了这个间歇性问题,与图像大小无关。

    首先,确保图像是 RGB 模式,绝对不是 CMYK 模式。根据我们的经验,RGB 渲染实际上更大。

    其次,在加载新图像之前,擦除(并在可能的情况下丢弃)图像容器中的任何先前图像,例如

    Control.Image = 无

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 2016-11-17
      • 2015-07-11
      • 1970-01-01
      • 1970-01-01
      • 2016-01-12
      • 1970-01-01
      相关资源
      最近更新 更多