views:

720

answers:

4

I remember i was loading in images by streaming it from the net straight into a bitmap. close the stream, return the bitmap and held it in an image control.

I excepted when i did = loadPicture() the first bitmap would be freed like a smart pointer would do in C++. But it didnt and i consumed a lot of ram until i called dispose. So my question is.

How does the GC and Dispose able objects work in C#? and why isnt it implemented like a smart_ptr?

+2  A: 

The GC kicks in when the runtime feels it is necessary.

The basic rule is: when you use an Disposable type (IDispose), then you (as the programmer) should release the resources used by that type as soon as possible, by calling Dispose when you do not longer need to use that type. For instance, when you read a file, you close that file as soon as you've done reading it. (Calling close will also call dispose in this case).

Frederik Gheysels
+6  A: 

References are not smart pointers. Letting a reference-variable go out of scope, replacing it with another value, and/or setting it with null all do exactly nothing.

This is simply part of the CLI /GC design...

Gargage Collection (GC) will run when needed, and should clean up the managed memory used, and (if a finalizer is provided) any unmanaged resources too. But for deterministic cleanup: that is the entire purpose of IDisposable. It is your job to Dispose() such objects when you have finished with them - either via using, or by handing it to something else which assumes this responsibility (common, for example, with streams/readers etc).

using (StreamReader reader = new StreamReader(myfile)))
{
   ...
}
Marc Gravell
+1. Im sure this question has been asked already :)
Dead account
Are you saying i don't 'need' to dispose of anything?
acidzombie24
Only in the sense that even if you don't, resources will eventually be freed (if they implement IDisposable correctly, that is). In practice, this is merely a safety net. You want to release unmanaged resources as soon as possible.
Anton Tykhyy
"...all do exactly nothing" is not the full truth. By setting references to null, you might allow the memory to be released earlier because the reference is no longer seen as "live" reference in the system when the GC runs.
Lucero
As far as I know, finalizers are not guaranteed to be called. Usually they are, but there's no garanty. See http://tinyurl.com/cr4uo2 (MSDN: Object.Finalize Method) under "Remarks" for details.
Mudu
@Lucero - I mean as an *active* operation; the runtime is indeed to do more clever things. @Mudu - indeed.
Marc Gravell
@acidzombe24 - choosing not to Dispose() things would be risky - you can run out of unmanaged resources without really using much managed memory, so GC wouldn't kick in. Early versions of VS suffered from this, IIRC. You should always try to release (Dispose()) such objects ASAP once you are finished with them.
Marc Gravell
@Lucero: If the variable is not referenced after the point where you set it to null, it's a no-op, because once you have reached the point where a variable is not referenced any more, the JIT knows and it is the same as leaving scope. Setting it to null is also JIT-optimized away.
configurator
A: 

smart_ptr are reference counted. While this allows for deterministic release of their resources when they are no longer referenced by any code, they do have their problems of their own: assigning references always requires the counter to be updated, circular references fail to be released automatically causing memory leaks, the memory manager is invoked more often.

The GC in .NET is a sweeping collector. It starts at any time when it feels that memory should be released (usually triggered by some memory usage condition, but not deterministic) and starts by building a list of all live references in the system (including the ones in CPU registers, nested references etc.). This works since we are in a managed environment where you cannot do pointer arithmetic etc. - the system can track all references. After the list of live references has been built, it basically releases all memory not known to be used anymore. Of course, this is just the basic sketch, for efficiency and management of unmanaged resources there is more to it like object generations, finalizers, etc., but that is not important for the basic understanding of how it works.

The IDisposable interface is used to implement the disposable pattern, which helps when you are working with objects that should be disposed in a deterministic way. The pattern is so that Dispose() is called explicitly when the object is no longer needed, therefore releasing unmanaged resources or closing handles etc., but not releasing its memory. This will be done by the GC later on, but it does not matter that this happens later, because the deterministic release of resources has already been performed.

Lucero
+1  A: 

You must call Dispose explicity on any object implementing IDisposable, otherwise your unmanaged resources will not be disposed. If you don't want to call it explicity, then you must override the Finalize method to call the Dispose method - that is why you will see this frequently:

 class MyClass : IDisposable
 {
    ...

    ~MyClass()
    { 
       this.Dispose(false);
    }

    public void Dispose()
    {
       this.Dispose(true);
       GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        { /* dispose managed stuff also */ }

        /* but dispose unmanaged stuff always */
    }
 }
Groo