6

Although I have been programming for about 11 years(mostly VB6, last 6 months C#), it's THE first time to actually ask a question :) I have found all my answers from teh interwebz but this issue i can't solve myself. Your site is among the most helpful places i have got the best answers from!

I will show the code i'm using (an extract of what's relevant). Problem is that when using RotateFlip method then the memory is increasing rapidly to ~200M and then get's collected by GC after some time. The main method calling it iterates about 30 times per second so the performance is of utmost importance here. I have tried using graphics matrix transform but this sometimes fails and shows non-flipped image. The application itself is based on using a webcam, hiding the preview, taking the callback picture and showing it in picturebox. It then overlays a rectangle on if from another class. That's the reason to use callback and not preview window.

Capture.cs class:

internal Bitmap LiveImage;

    int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
    {
        LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer);

        if (ExpImg) // local bool, used rarely when the picture saving is triggered
        {
            LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
            var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
                                    LiveImage.PixelFormat);
            using (a)
                a.Save("ocr.bmp", ImageFormat.Bmp);

        }
        else // dmnit, rotateflip leaks like h*ll but matrix transform doesn't sometimes flip :S
        {
            LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
            /*using (var g = Graphics.FromImage(LiveImage))
            {
                g.Transform = _mtx;
                g.DrawImage(LiveImage, 0, 0);
            }*/
        }
        GC.Collect(); // gotta use it with rotateflip, otherwise it gets crazy big, like ~200M :O
        return 0;
    }
}

In main form i have an event that's updating the picture in the picturebox:

private void SetPic()
{
    pctCamera.Image = _cam.LiveImage;
    _cam.PicIsFree = false;
}

Because i need to get the image to main form which is in another class then i figured the most logical is the exposed Bitmap which is updated on every callback frame. The reason i don't want to use matrix transform is because it's slower and sometimes with this speed it fails to flip the image and the frequency of such behavior is quite different with different PC's with different hardware capabilities and CPU speeds, also the fastest framerate 30fps with a 1.2GHz CPU shows this very frequently.

So, can you help me to figure it out? I'm not actually using it in current version, i'm using the commented-out matrix transform because i feel bad for using GC.Collect :(

Thank You!!!

VMAtm
  • 27,943
  • 17
  • 79
  • 125
sirWest
  • 63
  • 1
  • 4
  • Why do you repeat `LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);` code in each variant - you can move it out of the `if`-block – VMAtm Jul 07 '11 at 11:07
  • you are absolutely correct, actually i don't use it since i use the matrix but i forgot to re-check the logic after changing the code to the problem version. Thanks! – sirWest Jul 07 '11 at 11:11
  • Provided an answer, hope this helps. – VMAtm Jul 07 '11 at 11:18

3 Answers3

5
pctCamera.Image = _cam.LiveImage;

Heavy memory usage like you observe is a sure sign that you missed an opportunity to call Dispose() somewhere, letting the unmanaged resources (memory mostly) used by a bitmap get released early instead of letting the garbage collector do it. The quoted statement is one such case, you are not disposing the old image referenced by the picture box. Fix:

if (pctCamera.Image != null) pctCamera.Image.Dispose();
pctCamera.Image = _cam.LiveImage;
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • WOW, You are a true genius! This absolutely works, and drops the CPU usage also by at least 4% for a bonus! This issue has bogged me for months and you helped to solve this in 2 hours :) Thank You! – sirWest Jul 07 '11 at 12:55
  • Damn it! I've missed the easiest one. – VMAtm Jul 07 '11 at 19:53
1

You can rewrite your code like this:

internal Bitmap LiveImage;

int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
{
    using (LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer))
    {
        LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
        if (ExpImg) // local bool, used rarely when the picture saving is triggered
        {
            var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
                                    LiveImage.PixelFormat);
            using (a)
                a.Save("ocr.bmp", ImageFormat.Bmp);
        }
    }

    return 0;
}

Bitmap is an Image class, and implements the IDispose. As you create Bitmap each time, I suggest to use using statement for automatically freeing the resources.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
  • I just changed my code to your sample. I have tried to create one more extra Bitmap class for rotating and then disposing of it but it doesn't help. The memory still goes up like crazy, worse than before because of the extra bitmaps. Here's something similar i have tried: created new bitmap from initial pBuffer as a internal function Bitmap, flipped it with rotateflip, LiveImage = new Bitmap(tmpBMP); tmpBMP.Dispose(); This approach still leaks memory. If I do GC:Collect() each on callback which is very costly then it stays nicely low, no problems. – sirWest Jul 07 '11 at 11:37
  • Then I think the problem of your code is outside this pattern, may be pBuffer can causing it. – VMAtm Jul 07 '11 at 11:55
  • When i use the commented-out matrix transform function from Graphics class then the memory stays very stable without problems. So, cannot be. The pBuffer is a pointer to memory and nothing more. – sirWest Jul 07 '11 at 11:58
  • @sirWest: Yes. The memory will increase. **BUT IT WILL NOT "LEAK".** The garbage collector only runs when it needs to, using a complex series of internal optimization to figure out the best time to run. This is important, because garbage collection is an expensive operation, certainly not one that you want to be doing yourself. Give up on this: you're trying to solve a problem that doesn't exist. See [here](http://stackoverflow.com/questions/5191897/how-to-release-the-occupied-memory/5192350#5192350) for details. – Cody Gray - on strike Jul 07 '11 at 12:02
  • Note: In this case the problem was indeed not using Dispose where it was needed, but it also showed that for some reason the RotateFlip uses considerably more memory than other graphical functions I use in my application. It made it look like there was a leak, but there wasn't. Now we know: using it needs more caution for sake of memory :) – sirWest Jul 07 '11 at 18:16
-2

GC.Collect is there for this situation. Collecting the data is the ONLY way to free it and when creating HUGE bitmaps its the way to go. Does a GC.Collect really slow things down?

Other then that you should keep the number of bitmap copies as low as possible.

CodingBarfield
  • 3,392
  • 2
  • 27
  • 54
  • 1
    No, `GC.Collect` isn't actually there for you to use. This is a terrible suggestion. You need to dispose the `Bitmap` object, and nothing more. Let the garbage collector run on its own. The most important thing that you can do is understand that garbage collection in the managed world of .NET is a hands-off process for the programmer. – Cody Gray - on strike Jul 07 '11 at 12:01
  • not much really, at least i can't see the change in CPU usage in task manager. I was mostly afraid of calling it that often, i mean 30 times per second is a lot for every line of code, at least by my standards. I think that's the way to go then. – sirWest Jul 07 '11 at 12:05
  • ok i tried to dispose the bitmap on form code right after loading the picturebox with the new image like 'pctCamera = new Bitmap(_cam.LiveImage); _cam.LiveImage.Dispose();' the memory still leaks to about 280MB and then get's collected and so it loops. I still see it as some bug with using the GDI+ API in .NET's RotateFlip usage. The API is used directly but something leaves leftovers to memory. I have patched all other memory leaks and this is the final "hole" to fix before i can take out GC.Collect. – sirWest Jul 07 '11 at 12:11
  • @Cody Gray normally the GC knows what it does. But creating bitmaps and not calling GC.Collect can keep them around in virtual! memory creating a huge slowdown of you're application. Remember the GC is normally not forced to run EVER if you've got enough virtual memory around calling GC.Collect will keep the memory in use down and calling it a couple of time after creating huge objects has no obvious negative side effects. – CodingBarfield Jul 07 '11 at 13:13
  • That comment shows a complete lack of understanding of the philosophy of garbage collection. Yes, they could remain around in virtual memory, but you've failed to explain why that's a problem. I would suggest that you avoid advice to other people about memory issues until you understand the internal workings of those issues yourself. Calling `Dispose` is the solution here, not to manually invoke garbage collection. – Cody Gray - on strike Jul 07 '11 at 13:28
  • 1
    Years ago on older machines with 192mb of RAM keeping around a couple of 40mb sized bitmaps was really bad. Calling GC.Collect instead of Bitmap.Dispose cleared the memory faster and kept more free memory around. The Bitmap.Dispose function apparently did not tell the OS to actually clear the space. This is closer to 2000 then 2010 so implementations might have changed a lot. Garbage collection does not make memory management easier if you create a lot of data. The problem was that swapping huge amounts of memory to the hard disk(virtual memory) was creating too much overhead. – CodingBarfield Jul 07 '11 at 13:52