0

I'm trying to take a snapshot of the whole screen for reading pixel values. Actually i'm doing it without any problem. But after exactly 214 snapshots, i'm getting out of memory exception.

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);
}

I'm using this method with timer

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

It was giving invalid parameter exception. I solved it with "using". But now i'm stuck on this exception.

Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • 1
    Sounds like you're running a 32-bit process and scraping the 2GB limit. Why do you need all of these snapshots together in memory? Dispose the ones you don't need anymore. – Rotem Jun 29 '16 at 09:36
  • Similarities : http://stackoverflow.com/questions/4318563/c-sharp-out-of-memory-when-creating-bitmap –  Jun 29 '16 at 09:38
  • 1
    Are you disposing the Bitmap itself after you're done working with it ? – Ondrej Svejdar Jun 29 '16 at 09:39
  • 1
    @Stan: No, that link is about false OOM when using wrong parameters, which happens immediately. This is plain old real OOM I'd say.. – TaW Jun 29 '16 at 09:40
  • I don't want to hold all of these snapshots. Only one of them (the last one) is enough for me. How can i handle this? – Thunder Owl Jun 29 '16 at 11:05

1 Answers1

1

You need to dispose disposable resources once you're done working with them. Bitmap class implements IDisposable - so it is disposable resource. Correct pattern is instead of

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

Something like

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

Or in short form (which is preferable):

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

Reference: Using Objects That Implement IDisposable

Edit

You can easily emulate the issue:

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();
    }
}
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • If `Bitmap` implements the IDisposable correctly, shouldn't it dispose itself when there are no references any more? At least it would if it follows the destructor logic desribed here: http://stackoverflow.com/questions/538060/proper-use-of-the-idisposable-interface – Thomas Weller Jun 29 '16 at 11:21
  • @Thomas - have you read the accepted answer ? :) The Garbage collector is freeing JUST managed resources - so if you reference unmanaged memory (and Bitmap class does it) the allocated unmanaged memory will stay allocated until end of the process. – Ondrej Svejdar Jun 29 '16 at 13:00
  • No, look at `protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)`: it always frees unmanaged resources. – Thomas Weller Jun 29 '16 at 13:56
  • @Thomas - there seems to be misunderstanding on your end on how GC works. GC doesn't clean the object to which there are no references immediately (for performance reasons). GC fires where there is not enough managed memory. So when there are small managed objects that are allocating lot of unmanaged memory, you can easily get into trouble. Check out my snippet - that will get you there. Bitmap does have correct IDisposable pattern - however you have to use it properly. – Ondrej Svejdar Jun 29 '16 at 18:41
  • @Ondrej Svejdar thank you so much. The short form of "using" worked well. – Thunder Owl Jun 30 '16 at 06:00
  • Your snippet does not implement a Finalizer as in the linked post. Of course it has a "memory leak". I didn't look up the source of Bitmap, but it's easily possible that MS was not aware of the full consequences at the time they implemented IDisposable on the Bitmap. - Anyway, doing a `using` or calling `Dispose()` at the right time will certainly eliminate the issue and thus your answer is ok here. – Thomas Weller Jun 30 '16 at 06:18