【问题标题】:C# "Out Of Memory Exception" on returning variable accessorC#“内存不足异常”返回变量访问器
【发布时间】:2013-02-14 21:21:14
【问题描述】:

当我尝试新建一个位图并通过“get”访问器返回它时,我遇到了内存不足异常。位图大小为 640x480,深度为 Int32。

我怀疑 C# 垃圾收集器无法删除这些旧位图,因为它们正在访问器中返回给我的变量。我有超过 2GB 的可用空间,所以我不会想象这个“小”图像会占用太多内存。不幸的是,由于线程锁定问题,我不得不更新位图 (Trouble with locking an image between threads) 代码如下:

public Bitmap LiveFrame { get { return GetFrame(500); } }

.....

private Bitmap GetFrame(int timeout)
{
   Bitmap ret = null;
   //CLEyeCameraGetFrame places image data into this._PrivateBitmap
   bool success = CLEyeCameraGetFrame(_Camera, _PtrBmpPixels, timeout);  

   if(success) 
     ret = new Bitmap(this._PrivateBitmap);
    return ret;
}

关于非托管代码的注意事项: CLEyeCameraGetFrame 位于非托管 DLL 中。我在代码的前面使用 Marshal.AllocHGlobal 分配 _PtrBmpPixels 并且在关闭应用程序时释放之前不要触摸它。 _PtrBmpPixels 通过其接受 IntPtr 参数“scan0”的构造函数用于创建位图 _PrivateBitmap。因此,每当通过 CLEyeCameraGetFrame 更新 _PtrBmpPixels 时,也会更新 _PrivateBitmap。

我试图通过在重用 PcitureBox 位图之前对其进行处理来解决此问题,但这破坏了 PictureBox 的显示。我有两个线程更新两个不同的图片框/图像框:

lock (_CameraLocker)
{
  if (_VideoPlaying)
  {
    try{
         if (pbLiveFeed.Image != null)
            pbLiveFeed.Image.Dispose();

         pbLiveFeed.Image = _Camera.LiveFrame;
         pbLiveFeed.Invalidate();
       }catch (Exception ex) { }
}

....

lock (_CameraLocker)
{
   try{
        if (ibProcessed.Image != null)
          ibProcessed.Image.Dispose();
        procImage = new Image<Bgra, Int32>(_Camera.LiveFrame);
        procImage.Draw(new Rectangle(10, 20, 20, 15), new Bgra(1.0, 1.0, 1.0, 1.0), 5);

        ibProcessed.Image = procImage;
      }catch (Exception ex) { }
}

垃圾收集可能是造成这种情况的原因吗?从 get 访问器返回新对象是否不安全?

【问题讨论】:

  • 这应该是绝对安全的。底层本机 GDI 代码可能会在本机端引发异常,该异常会在托管端转换为(虚假的)内存不足异常。您的平台是否正在调用扫描仪或类似设备?
  • 旁注:考虑不要将属性用于预计需要相对较长时间的事情 - 改用方法。此外,您似乎不能将此属性用作属性(即item.LiveFrame.Width 具有创建将立即丢弃的“巨大”对象的副作用)。
  • 是的,实际上我是。该位图用于创建其他对象,然后在非托管代码中使用这些对象。因为我新建了非托管代码中使用的对象,所以我认为位图数据只是简单地复制过来,而不是放入非托管代码本身。
  • 创建这些Bitmap 实例后,您将如何处理它们?
  • Paul:我正在将它们分配给 PictureBox.Image,或者我正在用它们创建 Emgu.CV.Image 对象。谢谢阿列克谢指出这一点。这是对财产的明显滥用,我没有注意到这一点。不过,我仍然很好奇为什么会发生这种情况。

标签: c# bitmap garbage-collection computer-vision


【解决方案1】:

您问题中唯一真正的线索是缺少什么。您从未说过“并且我确保在使用后处理位图”。这是你在 .NET 编程中很容易忽略的东西,但是当你使用位图时会变得很烦。

Bitmap 类是一个非常小的包装类,它围绕着由 GDI+ 创建的 非托管 资源。实际的位图像素存储在非托管内存中。这就是该类具有 Dispose() 方法的原因,该方法释放了非托管内存分配。垃圾收集器几乎无法帮助您自动释放这些内存,Bitmap 对象太小而无法经常引发垃圾收集,以跟上程序消耗非托管内存的速度。结果是 oom-kaboom。

您需要查看其余代码并了解 LiveFrame 属性是如何被使用的。并确保返回的位图得到处理。例如,如果您将其分配给 PictureBox.Image 属性,那么您必须在分配之前处理旧图像。

【讨论】:

  • +1 听起来是时候拿出你的 Design Patterns 书重新思考一下了。
  • 啊,这听起来很对。我不会在任何地方处理它,所以我想不断更新肯定会导致这个问题。一旦我检查它,我会更新它。
  • 嗯,我使用位图的唯一时间是将它们分配给 PictureBox 和 EMGU ImageBox。我已经用显示我如何尝试处理位图的代码更新了主要帖子。但是,当我这样做时,它会破坏显示并在图片框中显示一个大的红色 X。
  • 我没有看到更新。在分配新的之前处理旧的,这样您就不会崩溃绘画代码。坠机产生了红十字。
  • 抱歉,已更新。在说我已经更新之前应该已经更新了,哎呀。
猜你喜欢
  • 1970-01-01
  • 2012-01-23
  • 2016-07-27
  • 2010-09-17
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 1970-01-01
相关资源
最近更新 更多