Roadmap › .NET Internals — CLR, CTS, CLS, IL, JIT, Memory Management & Garbage Collection › Module: Memory Management
Lessons in this course (11)
7. Finalization, IDisposable and the Dispose pattern
Intermediate★ Must knowLesson 7 of 11
The GC frees managed memory only. Unmanaged resources (file handles, sockets, database connections, OS handles, native memory) must be released explicitly — that's what IDisposable is for.
csharp
public sealed class ReportWriter : IDisposable
{
private readonly FileStream _file = new("report.txt", FileMode.Create);
private bool _disposed;
public void Write(string text) { ObjectDisposedException.ThrowIf(_disposed, this); /* ... */ }
public void Dispose()
{
if (_disposed) return;
_file.Dispose(); // release what we own
_disposed = true;
}
}
using var writer = new ReportWriter(); // Dispose() called at end of scope, even on exceptionsFinalizers (~ClassName()) run on a dedicated finalizer thread some time after the object becomes unreachable:
- Objects with a finalizer survive at least one extra GC (they go to the f-reachable queue), so they are more expensive.
- Order and timing are not guaranteed; finalizers may never run on process crash.
- Use them only as a safety net for directly owned unmanaged handles — and prefer
SafeHandle, which already implements this correctly.
The full Dispose pattern (for non-sealed classes that own unmanaged resources):
csharp
public class NativeBuffer : IDisposable
{
private IntPtr _ptr = Marshal.AllocHGlobal(1024);
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) { /* dispose managed members (other IDisposables) */ }
Marshal.FreeHGlobal(_ptr); // free unmanaged memory in both paths
_ptr = IntPtr.Zero;
_disposed = true;
}
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } // no need to finalize anymore
~NativeBuffer() => Dispose(false); // safety net if Dispose was forgotten
}IAsyncDisposable / await using for resources whose cleanup is asynchronous (streams flushing to network, DbConnection in async code).
| Dispose | Finalize | |
|---|---|---|
| Called by | Your code (using) | GC finalizer thread |
| Timing | Deterministic | Non-deterministic |
| Purpose | Release managed + unmanaged resources early | Last-chance release of unmanaged resources |
| Cost | Cheap | Extra GC survival, finalizer thread work |