Safely working with unmanaged handles
Reliably releasing unmanaged handles without constrained execution regions (CERs) is not possible. You always end up with a race condition, since a ThreadAbort
or OutOfMemory
exception can come in the middle of your finalizer. So you need to use CERs.
CriticalFinalizerObject
uses Constrained Execution Regions internally, is prioritized during finalization, and has some other features that make it relatively easy to "get it right" and always release unmanaged resources when they need to be. In general you should derive from SafeHandle
or CriticalFinalizerObject
when holding handles to unmanaged objects. That way you know they will be cleaned up when your object is garbage collected, even if it is an unusual shutdown situation like a ThreadAbortException
.
What using() and IDisposable adds
using()
and the IDisposable pattern allow you to release unmanaged resources eagerly - as soon as you are done using them. If you don't dispose your objects with using()
, they will stick around until the next garbage collection. This is inefficiency.
So CriticalFinalizerObject
is about making sure unmanaged resources are eventually released whereas using()
is about making sure unmanaged resources are released quickly. Both should be used together.
Also using()
is good to use in pure managed code, for example if you are caching a pool of objects the Dispose()
can return the object to the pool.
Compacting the heap during GC
Compacting the heap can be more efficient because the objects that last a long time end up all packed together. In NET Framework only the lowest generation (the most recently created objects) are compacted agressively. Theres is a lot of optimization done under the hood. It is fun to read up on, but not important for getting work done. If you want to read more about it, search for articles on "generational garbage collection."