Memory & Garbage Collection
18 questions found
How does garbage collection work in .NET?
Beginner
The GC automatically reclaims unreachable managed objects. It is generational (Gen 0/1/2): short-lived objects are collected cheaply in Gen 0, survivors are promoted.
var tmp = new byte[1024]; // Gen 0, collected quickly when unreachable
Real-world example
Most request-scoped allocations die in Gen 0, keeping collections cheap.
What is the difference between managed and unmanaged resources?
Beginner
Managed resources are handled by the GC; unmanaged resources (file handles, sockets, native memory) are not and must be released via Dispose/finalizers.
using var file = File.OpenRead(path); // wraps an unmanaged handle
Real-world example
Wrapping a native handle in an IDisposable so it's freed deterministically.
What is the IDisposable pattern and why implement it?
Intermediate
It provides deterministic cleanup of unmanaged resources via Dispose(), usually with using. Implement it when a type owns unmanaged or disposable resources.
public void Dispose() { _conn.Dispose(); GC.SuppressFinalize(this); }
Real-world example
A repository that owns a DbConnection implements IDisposable so callers can release it promptly.
What is the difference between a finalizer and Dispose?
Intermediate
Dispose is deterministic cleanup you call; a finalizer runs non-deterministically during GC as a safety net. Finalizers delay collection, so prefer Dispose and suppress finalization.
~Handle() { ReleaseNative(); } // last resort
public void Dispose() { ReleaseNative(); GC.SuppressFinalize(this); }
Real-world example
A wrapper around native memory uses a finalizer only as a backstop if Dispose was missed.
What causes managed memory leaks despite the GC?
Advanced
Objects stay reachable unintentionally: static references, un-removed event handlers, long-lived caches, or captured closures. The GC can't collect what is still referenced.
StaticCache.Add(bigObject); // never removed -> leak
Real-world example
An ever-growing static dictionary cache slowly exhausts memory in a long-running service.
How do Span<T> and ArrayPool reduce GC pressure?
Advanced
Span<T> gives allocation-free views over existing memory (arrays, stack, native); ArrayPool<T> rents and returns buffers instead of allocating. Both cut Gen 0 churn on hot paths.
var buffer = ArrayPool<byte>.Shared.Rent(4096);
try { Read(buffer); } finally { ArrayPool<byte>.Shared.Return(buffer); }
Real-world example
A high-throughput parser reuses pooled buffers to avoid allocating per request.
What is the difference between the stack and the heap in .NET memory management?
Beginner
The stack stores value-type local variables and method call frames, with fast, automatic, LIFO-ordered allocation/deallocation tied to method scope; the heap stores reference-type objects (and boxed value types), with allocation managed by the .NET runtime and deallocation handled by the garbage collector, not tied to any particular method's scope.
void Method() {
int x = 5; // value type: allocated on the stack
var person = new Person(); // reference type: 'person' variable on stack, actual object on the heap
} // 'x' and the 'person' reference are popped off the stack when Method() returns
Real-world example
Understanding why a local int variable is instantly reclaimed on method return, while a 'new Person()' object survives until the GC decides to collect it.
Common follow-ups: Why can objects on the heap potentially survive LONGER than the method call that created them?
Value vs Reference Types
What are .NET's garbage collection 'generations,' and what's the core assumption behind this design?
Beginner
The GC divides objects into Generation 0 (newest, short-lived objects), Generation 1 (a buffer generation), and Generation 2 (long-lived objects) — based on the 'generational hypothesis' that MOST objects die young, so the GC checks Gen 0 frequently and cheaply, only occasionally promoting survivors to older generations, which are scanned far less often.
// Conceptual: objects are checked increasingly less often as they survive collections
var temp = new byte[100]; // likely Gen 0, collected quickly if short-lived
static readonly Cache cache = new Cache(); // promoted to Gen 2 over time, rarely re-scanned
Real-world example
Understanding why creating many short-lived temporary objects in a loop is generally cheap, since Gen 0 collections are fast and frequent.
Common follow-ups: What triggers a full Generation 2 collection, and why is it considered much more expensive than a Gen 0 collection?
Fundamentals
How does the IDisposable pattern and the 'using' statement help manage UNMANAGED resources that the garbage collector doesn't automatically clean up?
Intermediate
The garbage collector only manages MANAGED memory — it doesn't know how to release unmanaged resources like file handles, database connections, or OS handles. IDisposable's Dispose() method provides a deterministic way to release those resources explicitly, and 'using' guarantees Dispose() is called automatically when the object goes out of scope, even if an exception occurs.
public class FileWrapper : IDisposable {
private readonly FileStream _stream;
public FileWrapper(string path) { _stream = File.OpenRead(path); }
public void Dispose() => _stream.Dispose(); // releases the unmanaged file handle
}
using (var wrapper = new FileWrapper("data.txt")) { /* ... */ } // Dispose() guaranteed to run
Real-world example
Ensuring a database connection, file handle, or network socket is promptly released rather than waiting for the GC's unpredictable timing.
Common follow-ups: What happens to an unmanaged resource if you forget to implement IDisposable and never explicitly release it?
File I/O & Streams
What is a memory leak in a garbage-collected language like C#, given that the GC handles cleanup automatically?
Intermediate
A memory leak happens when objects remain REACHABLE (through a live reference chain from a root, like a static field, an event subscription, or a cached collection) even though the application logically no longer needs them — since the GC only collects UNREACHABLE objects, an accidentally-retained reference prevents collection indefinitely, growing memory usage over time.
public class EventPublisher {
public static event Action? OnSomethingHappened; // static event: a common leak source
}
public class Subscriber {
public Subscriber() {
EventPublisher.OnSomethingHappened += HandleEvent; // if never unsubscribed, 'this' leaks forever
}
private void HandleEvent() { }
}
Real-world example
Debugging a long-running service whose memory usage climbs steadily, traced back to event subscriptions that were never unsubscribed.
Common follow-ups: Why are STATIC event subscriptions specifically a particularly common and dangerous source of memory leaks?
Delegates
Events & Lambdas