Garbage Collection And IDisposable

snarfblam

Mega-Ultra Chicken
Staff member
Joined
Jun 10, 2003
Location
USA
User Rank
*Expert*
This is just an overview of what a good DotNet programmer should know about garbage collection and disposable objects, prompted by this post.
alreadyused said:
  • When to handle the GC versus when not to
  • What category (managed/unmanaged) some of the most common controls fall into
  • How to test whether an object should be disposed
  • How to properly write a disposing method on classes with unmanaged resources
  • Simple tests to determine if your objects are being released from memory
  • Differences between C# and VB on disposing

Garbage Collection
There are plenty of thorough articles about garbage collection, so I will only quickly cover the basics. All class objects, or "reference type" objects, are tracked by the garbage collector. At certain points that the garbage collector deems safe, execution of a program is halted and the garbage collector identifies which objects are reachable and which objects arent. This is done by starting at certain "roots" and tracing references to see which objects are still connected to the program one way or another. These roots include static ("shared" in VB) variables, call stacks for all threads, unmanaged callbacks, and more.

The garbage collector also uses a generation system. When an object is created it is first generation. Each time a collection occurs, all objects that are checked but not collected move to the next generation (stopping at the third generation). Most garbage collections only check first generation objects, fewer check second generation objects, and fewer yet check third generation objects. This way, objects that are used for longer periods of time are checked less frequently.

Disposable Objects
The IDisposable interface exists primarily to allow you to explicitly release unmanaged resources (close files and connections and free Windows resources). You should implement this interface in the following scenarios:
  • When you directly acquire unmanaged resources (you will most likely be using some sort of interop in this case, such as COM or P/invoke)
  • When your object retains other disposable objects
  • When there is important finalization for your object that should be done as soon as possible.
All IDisposable objects should be disposed when you are done with them. That means that you will need to keep a reference to the object one way or another until you call Dispose. And just because you dispose an object doesnt mean that the garbage collector is done with it, so you should also release your reference to the object after you dispose it.

To Handle The GC Or Not To?
That is the question. Almost always, it is best to let the garbage collector do its own thing. (That is the answer.) The two reasons for this are, firstly, that the garbage collector is there so you dont have to worry about managing memory, and secondly, that it does its job well. So when should you explicitly invoke GC.Collect and force a garbage collection? You should do this when you are incredibly familiar with the garbage collection system, have decided that this will improve garbage collection performance, and are willing to perform thorough tests to confirm that you are indeed making an improvement by stepping in and bossing the garbage collector around.

If you are really interested in optimizing garbage collection here are some tips. When you are done with an object, set the reference to null to "disconnect" the object from your program and allow it to be collected (if the variable will go out of scope it isnt necessary to clear the reference, as long is it disappears one way or another). Dont cache objects unless it is very expensive to instantiate them. For smaller or simpler objects, it can often be best to use a structure instead of a class because the memory that they use is released immediately when the object goes out of scope. Just make sure that you understand how structures and classes behave differently.

What Category (Managed/Unmanaged) Do Most Controls Fall Into?
All Windows Forms controls use unmanaged resources because they wrap or are based on a Windows control. For the most part, all of your controls will be disposed automatically when the form is disposed (and the form is usually disposed when the user closes the form). The most common scenario where one might need to explicitly dispose of controls is when controls are dynamically created and destroyed.

Other common classes that are disposable are FileStreams, DataBase objects, RegistryKeys, and web connections.

How Do I Know If An Object Should Be Disposed?
The easiest way to test this programatically is to see if an object implements IDisposable.
Code:
[Color=Green]// C#[/Color]
[Color=Blue]if[/Color](someObject [Color=Blue]is [/Color]IDisposable)
    someObject.Dispose();

[Color=Green] VB[/Color]
[Color=Blue]If TypeOf[/Color] SomeObject [Color=Blue]Is[/Color] IDisposable [Color=Blue]Then[/Color]
    SomeObject.Dispose()
Generally you wont find yourself doing this, though, because the code that created or acquired the disposable object will track it and dispose it when necessary.

How To Properly Write A Dispose Method
First, the obvious. You should implement the IDisposable interface and create the Dispose method (sometimes it may be more appropriate to name this method a more descriptive name such as "Close" or "Release"). This method should release unmanaged resources and perform any necessary cleanup.

Secondly, the not-so-obvious. The Dispose method should be programmed such that calling it more than one time should not cause an error. Also, unless there is a reason not to, the Dispose method should call the GC.SupressFinalize method (specifying itself) to inform the garbage collector that this object needs no further finalization. In fact, the finalizer almost always only serves the purpose of a fall back in case an object is not disposed. In your finalize method you should invoke the Dispose method.

Many disposable objects use a slightly more complicated pattern. They implement IDisposable and have a Dispose (or Close) method, but they also have a private Dispose overload which accepts a boolean. The public Dispose method calls the private Dispose method and passes a true. The finalizer calls Dispose and passes a false. This boolean indicates whether the object is disposed explicitly (in code) or implicitly (by a finalizer). When the object is disposed explicitly extra cleanup can be performed, such as releasing references to other objects so that they may be garbaged collected sooner. This extra cleanup isnt necessary (especially when the object is disposed by the finalizer, since this often means that the program is closing anyways), but can improve garbage collection performance. This pattern should only be used when it represents a significant optimization.

Simple Tests To Determine If Your Objects Are Finalized And/Or Disposed.
A good profiler would probably be helpful in this situation. Off the top of my head, another possibility would be to add debug code to track objects. For instance, a debug build of an application could use some static variables to count how many objects of a particular type are created, how many are disposed, and how many are finalized. You could go one step further and use a static dictionary object which stores stack traces to find out where undisposed objects are created.

Language Differences And IDisposable.
The IDisposable interface is a .Net feature, not a language specific feature. Different languages have additional features, though, related to this interface. C# (all versions) and VB (version 8 and up) have "using blocks", where objects are disposed of when the scope of the using block ends or an exception is thrown.
C#:
using(FileStream someStream = new FileStream("C:\\textfile.txt", FileMode.Create)) {
    someStream.Write(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 11);
} // The stream will be closed when execution gets here.
C++/CLI uses a modified destructor syntax to implicitly implement IDisposable. It is also worth noting that C# uses destructor syntax to implement finalizers, where as VB requires that users explicitly override the Finalize method.

Any info that others wish to share is more than welcome.
 
Last edited by a moderator:
Top