Memory Management, GC and IDisposable
26.1 The Garbage Collector
A generational GC (Gen 0, 1, 2) plus the Large Object Heap (≥ 85 KB) and the Pinned Object Heap.
Modes: Workstation vs Server GC, concurrent/background GC (<ServerGarbageCollector>, <ConcurrentGarbageCollection> in .vbproj).
Roots: locals, statics (Shared), CPU registers, GC handles. Objects unreachable from the roots are collected.
GC.Collect() is almost never needed. Use GC.GetTotalMemory, GC.CollectionCount(gen), and GC.GetGCMemoryInfo() for diagnostics.
26.2 IDisposable and Using
The full dispose pattern (Protected Overridable Sub Dispose(disposing) + a finalizer) applies only to classes that own unmanaged handles directly. Prefer wrapping handles in SafeHandle.
IAsyncDisposable: VB can't use Await Using, so call Await obj.DisposeAsync() in a Finally block.
Public NotInheritable Class ReportWriter
Implements IDisposable
Private ReadOnly _writer As IO.StreamWriter
Private _disposed As Boolean
Public Sub New(path As String)
_writer = New IO.StreamWriter(path)
End Sub
Public Sub WriteLine(text As String)
ObjectDisposedException.ThrowIf(_disposed, Me)
_writer.WriteLine(text)
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
If _disposed Then Return
_writer.Dispose()
_disposed = True
End Sub
End Class
Using rw As New ReportWriter("report.txt")
rw.WriteLine("Hello")
End Using26.3 Common Leaks and Fixes
Event subscriptions to long-lived publishers RemoveHandler, weak events
Static (Shared) caches growing forever Use MemoryCache with eviction/size limits
Undisposed timers, streams, DB connections Using, dispose in Form.Dispose/OnClosed Closures capturing large objects Narrow the captured state Large arrays allocated repeatedly ArrayPool(Of T).Shared
WinForms controls created dynamically and never disposed Dispose them when removing from Controls
26.4 Diagnostics Tools
Visual Studio Diagnostic Tools (memory snapshots, diffs), dotnet-counters (GC metrics live), dotnet-gcdump, dotnet-dump + dumpheap (SOS), PerfView, JetBrains dotMemory.