Memory Management & Garbage Collection
Deep dive into .NET memory management — understand the garbage collector, memory allocations, and how to write memory-efficient code.
How .NET Garbage Collection Works
The .NET Garbage Collector (GC) automatically manages memory allocation and deallocation. It uses a generational model with 3 generations: Gen 0 (short-lived objects — collected most frequently, typically sub-millisecond pauses), Gen 1 (buffer between short and long-lived — collected less often), and Gen 2 (long-lived objects — collected infrequently but with longer pauses). Objects start in Gen 0 and are promoted to higher generations if they survive collection. The Large Object Heap (LOH) stores objects >= 85,000 bytes separately — LOH collection is expensive and causes fragmentation. GC modes: Workstation GC (optimized for responsive desktop apps, smaller heap), Server GC (optimized for throughput, parallel collection on multiple threads, larger heap per core). In .NET 8+, GC has DPAD (Dynamic Physical Address Partitioning) for better NUMA-aware allocation. Understanding GC helps you write code that minimizes allocation pressure and avoids GC pauses.
Reducing Memory Allocations
- Span<T> and Memory<T>: Work with contiguous memory without heap allocation — parse strings, process buffers without creating copies. Stackalloc for small arrays
- ArrayPool<T>: Rent and return arrays instead of allocating new ones — ArrayPool<byte>.Shared.Rent(1024). Dramatically reduces Gen 0 GC pressure
- StringBuilder: Use for string concatenation in loops — string + string creates new objects each time. StringBuilder modifies in-place
- ValueTask<T>: Use instead of Task<T> when operations often complete synchronously — avoids Task allocation overhead for hot paths
- Object Pooling: ObjectPool<T> for expensive-to-create objects — database connections, HttpClient, serializers. Create once, reuse many times
- Record Structs: Use readonly record struct for small, immutable value types — stack-allocated, no GC pressure. Perfect for DTOs in hot paths
- String Interning: Use string.Intern() for frequently repeated strings — stores one copy shared across references. Reduces duplicate string allocations
Memory Profiling Tools
- dotnet-counters: Real-time monitoring — GC collection counts, allocation rate, heap size, thread pool utilization. Lightweight, production-safe
- dotnet-dump: Capture and analyze memory dumps — find memory leaks by examining object roots, large objects, and reference chains
- dotnet-trace: Collect performance traces with ETW events — analyze GC events, allocations, exceptions, and thread activity
- Visual Studio Diagnostic Tools: Memory Usage profiler — take heap snapshots, compare allocations between snapshots, identify leaking types
- BenchmarkDotNet: Micro-benchmarking framework — measures execution time, memory allocation, and GC collections per operation with statistical rigor
- JetBrains dotMemory: Commercial profiler with visual heap analysis — sunburst diagrams, retention paths, real-time allocation tracking