C# Bitmap/Graphics 内存不足

C# Bitmap/Graphics Out of Memory

我正在尝试拍摄整个屏幕的快照以读取像素值。其实我这样做没有任何问题。但是在恰好 214 个快照之后,我出现内存不足异常。

Bitmap ScreenShot = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
  Screen.PrimaryScreen.Bounds.Height);

public Bitmap TakeSnapshot()
{
    Graphics graphic = null;
    Rectangle rect = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width,
      Screen.PrimaryScreen.Bounds.Height);

    using (graphic = Graphics.FromImage(ScreenShot))
    {
        graphic.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 
            0, 0, 
            ScreenShot.Size, 
            CopyPixelOperation.SourceCopy);
    }

    return ScreenShot.Clone(rect,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}

我将此方法与计时器一起使用

Bitmap bmp = TakeSnapshot();
        var c = bmp.GetPixel(0,0);

给出无效参数异常。我用 "using" 解决了它。但现在我陷入了这个例外。

您需要在使用完一次性资源后对其进行处置。 Bitmap class 实现了 IDisposable - 所以它是一次性资源。正确的模式是

Bitmap bmp = TakeSnapshot();
var c = bmp.GetPixel(0,0);

类似

Bitmap bmp = null;
try
{
  bmp = TakeSnapshot();
  var c = bmp.GetPixel(0,0);
  // any more work with bmp
}
finally
{
  if (bmp != null)
  {
    bmp.Dipose();    
  }
}

或简称(更可取):

using(Bitmap bmp = TakeSnapshot())
{
  var c = bmp.GetPixel(0,0);
  // any more work with bmp
}

Reference: Using Objects That Implement IDisposable

编辑

您可以轻松模拟该问题:

public class TestDispose : IDisposable
{
    private IntPtr m_Chunk;
    private int m_Counter;
    private static int s_Counter;

    public TestDispose()
    {
        m_Counter = s_Counter++;
        // get 256 MB
        m_Chunk = Marshal.AllocHGlobal(1024 * 1024 * 256);
        Debug.WriteLine("TestDispose {0} constructor called.", m_Counter);
    }

    public void Dispose()
    {
        Debug.WriteLine("TestDispose {0} dispose called.", m_Counter);
        Marshal.FreeHGlobal(m_Chunk);
        m_Chunk = IntPtr.Zero;
    }
}

class Program
{
    static void Main(string[] args)
    {
        for(var i = 0; i < 1000; i ++)
        {
            var foo = new TestDispose();
        }
        Console.WriteLine("Press any key to end...");
        Console.In.ReadLine();
    }
}