In C#, there are value and reference types. Value types are being stored in the stack, while reference types are being stored on the managed heap. When the new object (reference type) is created, it’s saved in the heap, and the garbage collector is later used to remove those no longer needed.
GC Generations
In C#, it’s working using the following generations:
- Generation 0 – new objects – all elements are being added to the gen 0 initially (note: there are some exceptions!), until it’s full or some memory limit is reached;
- Generation 1 – objects from gen 0 – once gen 0 is full (or GC is triggered in some other way), gen 0 is checked: objects that are no longer needed (have no references) are removed; objects that are still needed are usually moved to gen 1 (it depends on different factors, not always must be the case);
- Generation 2 – objects from gen 1 – when gen 0 is being checked, gen 1 is being checked as well, and objects from gen 1 that are still in use are being moved to gen 2;
- Large Object Heap (LHO) – some huge objects are stored directly in the LHO (sometimes called gen 3) – not to occupy so much space in gen 0.
GC Triggers
A garbage collector can be triggered by the following:
- Automatically (the finalize() method is being called automatically):
- when the gen 0 reaches the threshold set;
- when the system is running out of memory;
- some complex GC algorithms can also be used to determine when to trigger the GC;
- Manually:
- by calling dispose() method manually – e.g., for large test allocations when objects are no longer needed, it’s better to call the GC immediately on our own, rather than occupying the memory until the GC is triggered automatically. However, manually triggering GC is not recommended (since it can have an impact on performance).
The garbage collector is responsible for the heap part of managed memory:

Conclusion: In most cases, GC runs automatically. For the stack (value types), variables are deleted once the function is finished (by the LIFO – last in first out – principle). We just need to release references (like file, database connection) when done with objects that use them – by calling the dispose() method, or with the “using” statement (which calls dispose() by default).
Note: since the logic behind the GC is to remove the objects that are no longer in use, it’s important to keep in mind that each static variable would be kept in memory for the entire application lifetime. With that said, it’s important to avoid using static variables when they aren’t necessary, to avoid occupying the memory we actually don’t need all the time.