Can you have a memory leak in .NET despite garbage collection?

C# Memory Advanced 3–5 Yrs5+ Yrs Conceptual 1 min read

Quick Answer

Yes. The GC only reclaims objects that are unreachable. If something keeps a reference alive forever, that memory is never freed. Classic causes: a static/long-lived collection or cache that only grows, event handlers that aren't unsubscribed, a Timer or captured closure rooting an object, and undisposed types holding unmanaged resources.

Absolutely — garbage collection prevents dangling-pointer bugs, not leaks. The GC frees objects nothing references any more, so a leak in .NET is really “an accidental long-lived reference.” The one I see most is events: a singleton subscribes to something, or something subscribes to a singleton's event and never unsubscribes, so the subscriber can't be collected. Others are caches implemented as a plain dictionary that grows without bound, static collections, and timers that keep their callback target alive. To find them I take heap snapshots over time with dotnet-gcdump and look for object types whose count keeps climbing.

  • Event handler leak: a long-lived publisher holds a delegate to a short-lived subscriber → subscriber never collected. Unsubscribe, or use weak events.
  • Unbounded caches: use a size/time-bounded cache (MemoryCache with limits), not a raw Dictionary.
  • Static fields root everything they reference for the life of the process.
  • Diagnose with dotnet-counters, dotnet-gcdump, or a memory profiler; look for growth in Gen 2 / LOH and rising object counts across snapshots.