Demystifying Dispose


One commonly misunderstood feature is the relationship between disposable objects (Objects that implement the IDisposable interface) and the GC.  I know there are a lot of online resources about patterns and best practices but there is still a lot of confusion.  I remember going to a Dev Lab to answer some questions by Whidbey early adopters, and being asked at what point in an object’s lifetime does the GC call Dispose.  The answer: Never.

Dispose is just a method, like any other.  Its purpose is allow deterministic resource cleanup, much like C++’s destructors, but without freeing the object’s memory.  Like a C++ destructor, the developer puts whatever cleanup code is necessary to be performed when the object’s lifetime is up.  Unlike a destructor, the object lives on after being “disposed of”.  This means an object may be in an invalid state after being disposed, and developers should consider using ObjectDisposedExceptions when methods in a disposed object are called.

What happens when you call Dispose()

Any cleanup your object need done in a timely fashion should be done in Dispose.  Things like closing database connections, closing files, releasing bitmaps, etc.  Unmanaged resources in particular should be released in Dispose.  This is because the GC has no knowledge of anything not allocated on the managed heap.  For example, a Bitmap object that encapsulates a 2MB image file reports only its managed size (the size of the managed object) to the GC.  The GC knows nothing about the 2MB unmanaged image, thus will not collect it from memory.  By calling Dispose, you tell the Bitmap object that you are finished with it, and it will release the image itself.

What doesn’t happen when you call Dispose()

  • Calling Dispose does not prioritize the object for garbage collection. It simply unloads the object’s (unmanaged) resources from memory.
  • Calling Dispose does not deallocate the object from memory.  Only the GC does that when it performs a collection of the generation in which the object resides.
  • The CLR does not insert or run any code not in Dispose.  The behaviour of Dispose is defined by the developer.
  • Dispose must be called explicitly by the application.  It is never called by the runtime.  The only exceptions are when using C#’s using statement (see ShawnFa’s article for a good explanation of what exactly goes on), and the foreach keyword in C# will result in Dispose being called on the Enumerator
  • Dispose is NOT threadsafe.  This means two threads can call Dispose on the same object at the same time.  Like for any other synchronization-sensitive method, take steps to make sure this doesn’t happen.  I’ll give an example of when this might happen in the next section.

What about the finalizer?

There are a few reasons why not to rely on the finalizer to clean up resources that I’ll cover in a future blog entry on finalizers.  The main reasons you should be concerned with are performance and determinism.  Finalizers are expensive to run, and there’s no order (or even guarantee) that they will be run (for example, the finalizer thread may time out, or be killed on AppDomain unload).

That being said, the finalizer should be your last chance to clean up resources, in case someone using your class forgets to dispose of it when done.  A call to Dispose inside the finalizer reduces duplication of code.  However, you want to make sure the resources don’t get released twice.   In your Dispose method, you want to make sure you call GC.SuppressFinalize(this) after your clean up code.  If you suppress the finalizer before the clean up, you’re limiting your ability to recover from failures during the cleanup.

Some of you may have noticed a race condition in my description above.  Consider a situation where the call to Dispose is the last time your object is being referenced.  As soon as Dispose is entered, the object is eligible for collection by the GC.  Before that happens, the object’s finalizer gets called, which calls Dispose.  You now have two threads inside Dispose, possibly double-freeing resources (this could be bad, as in the case of GCHandles).  Make sure you follow the pattern I linked to above and use a disposing flag to avoid problems like this.

For more information about an object being collected while one of its methods are being run, check out these (very complete) blog posts by Chris Brumme: (Finalization and Lifetime, GC.KeepAlive, handle recycling).

If your .NET application is using unmanaged resources, make sure to implement the Dispose pattern, secure in the knowledge that nothing magical is going on.

EDIT: Added point about Dispose and foreach. Thanks Steve!

Edit:  Minor corrections.

Comments (36)

  1. Anonymous says:

    How come an object can be finalized while my code is still holding a reference to it and executing a method (Dispose()) on that reference? If that’s true then there’s a major bug in the GC.

  2. Anonymous says:

    Hi Jerry,

    if your code is holding a reference to the object is question, it will not be collected, and thus not finalized. However, an object is eligible for collection after the last reference to that object, which may be inside one of the object’s methods.

    It’s not a bug in the GC, just a oft-misunderstood subtlety. Please read Chris Brumme’s linked posts about Finalization to get the whole explanation.

  3. Anonymous says:

    Your first point,

    " It simply unloads the object’s (unmanaged) resources from memory."

    Surely that’s not true. The way that reads (to me) is the unloading is automatic, when it’s actually down to the code within the dispose implementation.

  4. Anonymous says:

    Hi Barry,

    Dispose does whatever you put in it. It’s meant to clean up unmanaged resources, whether it be unloading bitmaps from memory or closing sockets. You’re right, there’s nothing "automatic" about it. Sorry if that sounds confusing.

  5. Anonymous says:

    ~finalizer() is automatic, correct? I want finalizer() to call Dispose() to make sure the database connection has been closed but I get errors.

    Can anyone point me to a resource that might help me accomplish this? Example maybe?

    Good topic to blog.

  6. Anonymous says:

    I think most of the confusion around Dispose is because of the lack of good documentation on it – most users see the System.Windows.Forms.Control class and how it’s finalizer calls the special void Dispose(bool disposing) method. I did eventually figure it out, but when you start it’s fairly confusing – the documentation is geared more towards a very advanced developer who also understands how the GC works.

    In your next article I’d really like to see a complete list of the times where the Finalizer is not called. I can’t find the information anywhere – the MSDN documentation seems to imply that it randomly isn’t called during normal usage – when my hunch is that it’s a not a very common thing. Having the Finalized not guarunteed is a flaw in .NET, IMHO – if you don’t clean up unmanaged resources – you have a memory leak. When the documentation basically says "oh well" it kind of makes developers cranky 🙂

    I’m also surprised that not many developers discuss HandleRef and when it should be used. In which cases should I use HandleRef as opposed to IntPtr when calling unmanaged APIs?

  7. Anonymous says:

    Chris, that’s what I thought. In that case your blurp about synchronizing Dispose() when you call it from the finalizer is pointless. Either your code will call Dispose() explicitly, and in that case the finalizer will not run since you’re still holding a reference or Dispose() will be called from the finalizer, at which point nobody holds a reference to call it explicitly and since you said the runtime won’t call it for you there’s no way anybody else could call it at the same time.

    There’s still nothing to stop the user from calling Dispose() twice but then, most of the standard class library is not thread safe either.

  8. Anonymous says:

    Jerry, please read Chris Brumme’s post about finalization, in particular the section titled "Your object is callable during Finalization". Chris is the father of the CLR’s finalization implementation and can explain it much better than I can 🙂

  9. Anonymous says:

    Hi ShadowChaser,

    I agree that there isn’t enough documentation about Dispose and the GC; that’s why I started this blog 🙂

    The times when finalizers don’t get called are enumerated in Chris’ posts. Basically, if the finalizer thread is starved, or the appdomain/process is rudely aborted, your finalizer may not run. In Whidbey (v2.0), we’ve implemented Critical Execution Regions (CERs) that help mitigate these issues.

    As far as HandleRefs, maybe Adam Nathan can better answer your questions: http://blogs.msdn.com/adam_nathan/

  10. Anonymous says:

    Chris Brumme explains it, and it does not say that the object is callable during finalization. He does go into details and all he says that Dispose can be called multiple times from different threads (which I meantioned and which should really be taken care of the app, not your object) or that finalizer can be called while the resource is still used by other code, detached from the original object – which won’t be solved by synchronizing Dispose() anyways.

    My point is that you should not worry about Dispose being called twice at the same time (not twice at different times), unless you want to make your object completely thread safe. And the vast majority of objects, including in the BCL are not and do not have to be thread safe.

  11. Anonymous says:

    Chris says if you resurrect your object "…application threads and the finalizer thread can simultaneously be active in your object". This unfortunately includes calling the Dispose method.

    You shouldn’t have to worry about this situation unless you’re planning to resurrect objects. Like you said, the BCL does not do this, because in many cases, a resurrected object is not in a valid state.

    To make a long story short (too late):

    -Don’t use finalizers

    -If you have to, don’t resurrect

    -If you have to, consider making your object’s methods thread safe

  12. Anonymous says:

    Cbrumme didn’t go into much detail, but how does one resurrect objects?

  13. Anonymous says:

    If you assign a live reference to an object’s this pointer in the finalizer, the object will be taken off, the put back on the finalization queue (or if you call GC.ReregisterForFinalize() on an object).

    I’ll save a discussion of resurrection for another blog entry.

  14. Anonymous says:

    Jerry,

    <quote>

    My point is that you should not worry about Dispose being called twice at the same time (not twice at different times), unless you want to make your object completely thread safe. And the vast majority of objects, including in the BCL are not and do not have to be thread safe.

    </quote>

    http://www.harper.no/valery/PermaLink,guid,6a9b1b31-166d-4ac8-a91c-0eab48234359.aspx appears to state that the Dispose can be called by the finaliser unless you put a ‘this’ reference at the end of Dispose… In which case you may need to worry about Dispose being called twice at the same time…

  15. Anonymous says:

    "…and the foreach keyword in C# will result in Dispose being called on the enumerated object if the IEnumerator-implementing class that’s being enumerated implements IDisposable."

    Really? I didn’t know that… is there any way to suppress this behavior?

  16. Anonymous says:

    Matt, to be clear, it’s the Enumerator that’s being Disposed, not the elements of the Enumeration:

    http://weblogs.asp.net/brada/archive/2004/06/25/166527.aspx

  17. Anonymous says:

    S dot One heeft het over .NET &raquo; Uitleg over Dispose

  18. Anonymous says:

    Hi Chris

    BEGIN QUOTE

    However, an object is eligible for collection after the last reference to that object, which may be inside one of the object’s methods.

    END QUOTE

    I understand that GC collects object not referenced directly or indirectly by a root (global/static variables, local or parameter variables on thread stacks, the cpu registers that refers to reference type objects). The ‘this’ pointer has to be either on the stack or in a CPU register, so it is considered a root. Am I correct?

  19. Anonymous says:

    After reading a bunch of different articles and implementations I have finally come to terms with the…

  20. Anonymous says:

    Hi,

    I’m using VB .NET and I’m trying to free resources from this class but I can’t seem to do it. Everyone’s help file says, "Put Clean up code here in place of

    ‘ If disposing equals true, dispose all managed

    ‘ and unmanaged resources." But no one gives an example of what clean up code looks like. is it just:

    Line_Set = ""

    or is it something else. I have no idea, and no one seems to write about it.

    My email address is adam.volk@consultant.volvo.com

    Thanks

    Chris

    Public Class truck_entry

    ‘Public Class BaseResource

    Implements IDisposable

    ‘ Pointer to an external unmanaged resource.

    Private handle As IntPtr

    ‘ Track whether Dispose has been called.

    Private disposed As Boolean = False

    ‘ Other managed resource this class uses.

    ‘ Private Components As Component

    Private Line_Set_Str As String

    Private Location_Str As String

    Private Worker_1_Str As String

    Private Worker_2_Str As String

    Private Alert_Str As String

    Private Category_Str As String

    Private Model_Str As String

    Private Time_Date As Date

    Private Due_time_Dbl As Double

    Private Comment_Str As String

    Private I As Int16

    ‘ Constructor for the BaseResource Object.

    Public Sub New()

    ‘ Insert appropriate constructor code here.

    End Sub

    ‘ Implement IDisposable.

    ‘ Do not make this method Overridable.

    ‘ A derived class should not be able to override this method.

    Public Overloads Sub Dispose() Implements IDisposable.Dispose

    Dispose(True)

    ‘ Take yourself off of the finalization queue

    ‘ to prevent finalization code for this object

    ‘ from executing a second time.

    GC.SuppressFinalize(Me)

    End Sub

    ‘ Dispose(disposing As Boolean) executes in two distinct scenarios.

    ‘ If disposing is true, the method has been called directly

    ‘ or indirectly by a user’s code. Managed and unmanaged resources

    ‘ can be disposed.

    ‘ If disposing equals false, the method has been called by the runtime

    ‘ from inside the finalizer and you should not reference other

    ‘ objects. Only unmanaged resources can be disposed.

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)

    ‘ Check to see if Dispose has already been called.

    If Not (Me.disposed) Then

    ‘ If disposing equals true, dispose all managed

    ‘ and unmanaged resources.

    If (disposing) Then

    ‘ Dispose managed resources.

    ‘Components.Dispose()

    End If

    ‘ Release unmanaged resources. If disposing is false,

    ‘ only the following code is executed.

    ‘CloseHandle(handle)

    handle = IntPtr.Zero

    ‘ Note that this is not thread safe.

    ‘ Another thread could start disposing the object

    ‘ after the managed resources are disposed,

    ‘ but before the disposed flag is set to true.

    ‘ If thread safety is necessary, it must be

    ‘ implemented by the client.

    End If

    Me.disposed = True

    End Sub

    ‘ This Finalize method will run only if the

    ‘ Dispose method does not get called.

    ‘ By default, methods are NotOverridable.

    ‘ This prevents a derived class from overriding this method.

    Protected Overrides Sub Finalize()

    ‘ Do not re-create Dispose clean-up code here.

    ‘ Calling Dispose(false) is optimal in terms of

    ‘ readability and maintainability.

    Dispose(False)

    End Sub

    ‘ Allow your Dispose method to be called multiple times,

    ‘ but throw an exception if the object has been disposed.

    ‘ Whenever you do something with this class,

    ‘ check to see if it has been disposed.

    Public Sub DoSomething()

    If Me.disposed Then

    ‘Throw New ObjectDisposedException

    MsgBox("Dispose Called Too Often")

    End If

    End Sub

    Public Property ID() As Int16

    Get

    Return I

    End Get

    Set(ByVal Value As Int16)

    I = Value

    End Set

    End Property

    Public Property Line_Set() As String

    Get

    Return Line_Set_Str

    End Get

    Set(ByVal Value As String)

    Line_Set_Str = Value

    End Set

    End Property

    Public Property Location() As String

    Get

    Return Location_Str

    End Get

    Set(ByVal Value As String)

    Location_Str = Value

    End Set

    End Property

    Public Property Worker_1() As String

    Get

    Return Worker_1_Str

    End Get

    Set(ByVal Value As String)

    Worker_1_Str = Value

    End Set

    End Property

    Public Property Worker_2() As String

    Get

    Return Worker_2_Str

    End Get

    Set(ByVal Value As String)

    Worker_2_Str = Value

    End Set

    End Property

    Public Property Alert() As String

    Get

    Return Alert_Str

    End Get

    Set(ByVal Value As String)

    Alert_Str = Value

    End Set

    End Property

    Public Property Category() As String

    Get

    Return Category_Str

    End Get

    Set(ByVal Value As String)

    Category_Str = Value

    End Set

    End Property

    Public Property Model() As String

    Get

    Return Model_Str

    End Get

    Set(ByVal Value As String)

    Model_Str = Value

    End Set

    End Property

    Public Property Time() As Date

    Get

    Return Time_Date

    End Get

    Set(ByVal Value As Date)

    Time_Date = Value

    End Set

    End Property

    Public Property Due_time() As Double

    Get

    Return Due_time_Dbl

    End Get

    Set(ByVal Value As Double)

    Due_time_Dbl = Value

    End Set

    End Property

    Public Property Comment() As String

    Get

    Return Comment_Str

    End Get

    Set(ByVal Value As String)

    Comment_Str = Value

    End Set

    End Property

    ‘Public Sub dispose()

    ‘ GC.Collect()

    ‘End Sub

    Public Sub Clear()

    Line_Set = ""

    Model = ""

    Category = ""

    Location = ""

    Worker_1 = ""

    Worker_2 = ""

    Alert = ""

    Due_time = 0

    ‘time = ##

    Comment = ""

    End Sub

    End Class

  21. Anonymous says:

    After reading a bunch of different articles and implementations I have finally come to terms with the…

  22. Anonymous says:

    Is any of this still relevant for VB2005?  IF, in VB.Net 2003 I had my Finalize() method calling Dispose() (just in-case the caller forgot to do it explicitly), then do I still need to do this?  I have heard rumours that the 2005 compiler automatically puts in code to call Dispose() – that Dispose() effectively IS the finaliser now?? is this complete bollocks or is there some truth in it?

  23. clyon says:

    Dr. Evil,

    As far as I know, the VB compiler did not change the Dispose/Finalizer symantics for VB.NET 2005.  Please point me to any references that claim this so I can investigate.

    Thanks

    -Chris

  24. Anonymous says:

    If "Dispose is just a method, like any other." and needs to be called explicitly why do we need to inherit from IDisposable interface? We could very well write our own method and call that explicitly from application and finalizer. Does this has to do something with using{…}?

    Please advice.

    Thanks

    Rohit

  25. clyon says:

    Hi Rohit

    There are a few reasons why you would want to inherit from IDisposable.  As you pointed out, the using block requires that your object implement it.  Another good reason is that consumers of your class can find out in an easy and uniform way whether your object exposes such a method by attempting to cast as IDisposable.  Imagine having an ArrayList of objects and some of which need to be disposed.  Using the "as" C# keyword will allow you to easily dispose of the objects that need it.

    Hope that helps.

    -Chris

  26. Anonymous says:

    So in a previous post, we talked about Understanding when to use a Finalizer in your .NET class so now