2

I caught up in this problem.

Garbage Collector not doing its job. Memory Consumption = 1.5GB & OutOFMemory Exception

I feel that there is something wrong in my understanding. Please clarify these things.

  1. Destructor & IDisposable.Dispose are two methods for freeing resources that are not not under the control of .NET. Which means, everything except memory. right?
  2. using blocks are just better way of calling IDisposable.Dispose() method of an object.

This is the main code I'm referring to.

class someclass
{
    static someMethod(Bitmap img)
    {
        Bitmap bmp = new Bitmap(img); //statement1
        // some code here and return
    }
}

here is class I'm using for testing:

class someotherClass
{
    public static voide Main()
    {
        foreach (string imagePath in imagePathsArray)
        {
            using (Bitmap img1 = new Bitmap(imagePath))
            {
                someclass.someMethod(img1);
                // does some more processing on `img1`
            }
        }
    }
}

Is there any memory leak with statement1?

Question1: If each image size is say 10MB. Then does this bmp object occupy atleast 10MB? What I mean is, will it make completely new copy of entire image? or just refer to it?

Question2:should I or should I not put the statement1 in using block?

My Argument: We should not. Because using is not for freeing memory but for freeing the resources (file handle in this case). If I use it in using block. It closes file handle here encapsulated by this bmp object. It means we are also closing filehandle for the caller's img1 object. Which is not correct?

As of the memory leak. No there is no scope of memory leak here. Because reference bmp is destroyed when this method is returned. Which leaves memory it refered without any pointer. So, its garbage collected. Am I right?

Edit:

class someclass
{
    static Bitmap someMethod(Bitmap img)
    {
       Bitmap bmp = new Bitmap(img); //can I use `using` block on this enclosing `return bmp`; ???
        // do some processing on bmp here
       return bmp;
    }
}
Community
  • 1
  • 1

3 Answers3

1

You are equating memory to managed resources, which is not necessarily the same thing at all.

If you create a new object of a type that implements IDisposable, you should dispose it when you are done with it. Period.

The truth of the matter is, unless you disassemble the Bitmap class with something like Reflector, you won't know how it handles the other instance passed in its constructor, or what its Dispose method actually does. The inclusion of the IDisposable interface should be seen as an instruction from the developer of the class to call the Dispose method when you're done with it. Your previous question says you are observing a memory leak; we have pointed out that you are not disposing everything - have you tried adding the dispose calls we suggested and seeing if the memory leak issue is resolved?

David M
  • 71,481
  • 13
  • 158
  • 186
  • >>"If you create a new object of a type that implements IDisposable, you should dispose it when you are done with it. Period." wouldn't this affect other objects? – imageWorker Apr 01 '10 at 13:17
1

Memory leaks are very rare in .NET. What you have is a resource leak (GDI). Technically, these resources are "memory", but they come from a special pool of reserved memory which is considerably smaller than the managed object heap (where most of your object data is stored), and the .NET Garbage Collector does not manage this specific memory.

Not disposing a Bitmap in the same routine in which it's created does not automatically cause a resource leak. In fact, since the class has a finalizer, it's possible that it will get cleaned up within a reasonable time; the problem is, you don't know when, and if you create too many Bitmap objects without cleaning them up (with Dispose), you risk starving the OS of GDI resources, and you end up with bizarre behaviour system-wide, like not being able to restore minimized windows or text not being displayed correctly on the screen.

When you work with IDisposable objects, what you need to make sure of is that you dispose them eventually, when you are done with them. There's nothing wrong with the following code:

Bitmap CreateBitmap(...)
{
    Bitmap bmp = new Bitmap(400, 400);
    // Do something with the bitmap
    bmp.SetPixel(1, Color.Red);
    // etc.
    return bmp;
}

Both the method name and return type of this method clearly indicate that it is allocating but not freeing resources. It is creating an object that holds on to unmanaged resources, and it is up to the caller to clean up. As long as you use the method this way:

using (Bitmap bmp = CreateBitmap(...))
{
    // Do something else with the bitmap
}

...then you will be fine.

What is a semantic error is creating a Bitmap and simply letting the reference fall out of scope:

public int PossiblyLeakyMethod()
{
    Bitmap bmp = CreateBitmap(...);
    return bmp.Height;
}

This is bad. Now there's nothing referencing the Bitmap anymore. The garbage collector will probably decide to finalize it pretty soon, since it didn't live long enough to get promoted from Gen0. But it still has heap semantics here, which means that it does not get finalized when it falls out of scope (i.e. when the method finishes executing).

As I've mentioned in other answers, you also aren't supposed to assume anything about the private implementations of objects when you work with IDisposable. Most core framework IDisposable classes will have finalizers, but somebody else's library might have a broken implementation without a finalizer, in which case the "possibly" leaky method above would be guaranteed to leak.

Even when a finalizer exists, it's entirely possible to create objects at a faster rate than the GC can handle:

public void Crash()
{
    while (true)
    {
        Bitmap bmp = new Bitmap(1920, 1200);
    }
}

Leave this running and see how long it takes to get an OutOfMemoryException. It doesn't really matter that Bitmap has a finalizer here; the problem is that the "memory" you're running out of isn't the same memory that the GC is managing, and the finalizers aren't running fast enough to free all of the unmanaged resources. The GC only kicks in when you're running out of managed memory.

Last point here: It's also an error to dispose and then return an object. The following code will crash:

public Bitmap CreateUselessBitmap()
{
    using (Bitmap bmp = new Bitmap(400, 400))
    {
        // Do something with bitmap
        return bmp;
    }
}

Here you're returning something that's already been disposed. If the caller tries to do anything at all of importance with the Bitmap you return, it will fail.

So you need to make sure you Dispose your IDisposable objects, but only when you are actually finished using them. If a method's purpose is to create a Bitmap that is intended to be used for something else later on, then don't put it in a using statement in that method, but do name and document the method to clearly indicate that it allocates resources, so that the programmer who writes code to call that method knows that he needs to Dispose the result.

Hope that clears up a few things.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • Excellent!! All doubts cleared about using blocks. But I want to learn more about these `resources` , `memory` , `GDI` objects. Can you point me to some good material? – imageWorker Apr 01 '10 at 14:53
  • Summary of GDI Objects: http://msdn.microsoft.com/en-us/library/ms724291(VS.85).aspx. The Wiki page on Garbage Collection is a good start for the memory vs. non-memory resources distinction: http://en.wikipedia.org/wiki/Garbage_collection_(computer_science) - in particular the first few paragraphs and the section on determinism. Also here: http://msdn.microsoft.com/en-us/magazine/bb985010.aspx (from "Finalization" onward). – Aaronaught Apr 01 '10 at 15:12
  • I've read you answer again & again. According to you I got OOM here http://stackoverflow.com/questions/2559826/garbage-collector-not-doing-its-job-memory-consumption-1-5gb-outofmemory-exc not because of because I'm leaking managed heap memory but because of leaking resources (which are also memory) that is not managed by .NET. right? I absolutely won't agree. Because I'm getting OOM exception after running it over max 20 images (bmps) on each bmp. I'm passing each bmp to 4 static methods. Even if I assume all are leaking resources. 20*4 = 80 resources which wouldn't consume 1.5GB. because.. – imageWorker Apr 01 '10 at 17:23
  • because.. when I run your infinite loop of allocating new Bitmaps and observe memory process its like ( 200mb , 300mb, 700mb, 300mb, 700mb, 1.2gb, 600mb, etc..) its increasing and decreasing but it might have leaked like a million resources & grabage collector is taking care of it. but the with the code in other question memory occupied never decreases. Its strictly increasing. – imageWorker Apr 01 '10 at 17:26
  • @imageWorker: Whether you agree or not, that's what it is. Maybe the bitmaps in that question are bigger, and you're also creating more of them at the same time in the same scope. I don't know where the 1.5 GB number comes from - if you're looking at the number in the Task Manager and comparing it to your physical RAM, that's not a valid test because it doesn't distinguish physical RAM from virtual address space and because GDI handles are limited by more than just the available RAM (and when you run out of handles, you still get an "out of memory" error). – Aaronaught Apr 01 '10 at 18:06
  • Of course, as another person already pointed out in *that* question, it's *possible* the OOM is happening for different reasons, but that doesn't change a thing about *this* question or *this* answer. – Aaronaught Apr 01 '10 at 18:07
0

Destructor & IDisposable.Dispose are two methods for freeing resources that are not not under the control of .NET. Which means, everything except memory. right?

Yes, you are right.

using blocks are just better way of calling IDisposable.Dispose() method of an object.

They are not a 'better' way. Just more convenient sometimes.

Is there any memory leak with statement1?

I don't think so, though i am not sure.If a bitmap contains a finalizer, then it will be eventually invoked.

P.S. I think this article can help you.

n535
  • 4,983
  • 4
  • 23
  • 28