memory-leaks-and-diagnosticslisted
Install: claude install-skill Sarmkadan/dotnet-senior-skills
# Memory Leaks and Diagnostics
## Managed leaks are reachability bugs
The GC collects what is unreachable; a ".NET memory leak" is always something still holding a reference - usually a long-lived object (static, singleton, cache) pointing at things meant to be short-lived. Review long-lived state with one question: "what removes entries from this?"
## Event handlers: the classic
`publisher.Event += subscriber.Handler` stores a reference to the subscriber inside the publisher. Long-lived publisher + short-lived subscriber = every subscriber ever, retained:
```csharp
// WRONG: scoped service subscribing to a singleton's event - one leaked scope graph per request
public OrderProcessor(GlobalEvents events) => events.OrderPlaced += OnOrderPlaced;
// RIGHT: unsubscribe symmetrically - which means the type is IDisposable
public void Dispose() => _events.OrderPlaced -= OnOrderPlaced;
```
Review flags: any `+=` on an event of a longer-lived object without the matching `-=` in Dispose; `static event` (subscribers live until process exit); lambda subscriptions (`events.X += (s, e) => ...`) that can never be unsubscribed because the delegate instance is gone. Prefer not having the mismatch at all: singleton-to-singleton events are fine; cross-lifetime notification is better served by `Channel<T>`/messaging than events.
## The other repeat offenders
- **Static/singleton collections as ad-hoc caches**: `static Dictionary<Guid, UserState>` with adds and no eviction is a leak with a