C# Memory Management and Garbage Collection
1. What is Garbage Collection in .NET?
Garbage Collection (GC) is an automatic memory management process in .NET that releases memory occupied by managed objects that are no longer reachable by the application.
Example:
class Employee
{
public string Name { get; set; }
}
void CreateEmployee()
{
Employee emp = new Employee();
emp.Name = "John";
}
Explanation:
-
An
Employeeobject is created on the managed heap. -
The
empvariable references that object. -
After the method finishes, the local reference disappears.
-
If no other references exist, the object becomes eligible for garbage collection.
-
The GC reclaims its memory during a future collection.
Key Points:
-
GC is managed by the .NET CLR.
-
Automatically reclaims memory of unreachable managed objects.
-
Developers generally do not need to manually free managed memory.
-
GC.Collect()can request garbage collection but should normally be avoided. -
GC does not guarantee immediate memory release.
2. How Does the Garbage Collector Work?
The Garbage Collector identifies objects that are still reachable and reclaims memory occupied by unreachable objects.
GC Process:
Application creates objects
|
v
Objects allocated on managed heap
|
v
GC determines collection is needed
|
v
Identify reachable objects
|
v
Mark reachable objects as live
|
v
Reclaim memory of unreachable objects
|
v
Compact memory (when applicable)
|
v
Application continues
Example:
class Customer
{
public string Name { get; set; }
}
Customer c1 = new Customer();
Customer c2 = new Customer();
c1 = null;
Explanation:
-
Two objects are created.
-
c1 = nullremoves one reference. -
If no other references exist, the first object becomes eligible for GC.
-
The second object remains reachable through
c2.
Key Points:
-
GC uses reachability analysis to identify live objects.
-
GC roots include local references, static references, and other runtime roots.
-
GC typically uses marking and compaction for the small object heap.
-
Garbage collection can temporarily pause managed execution.
-
Setting a variable to
nulldoes not immediately trigger GC.
3. What Are Generation 0, Generation 1, and Generation 2?
.NET uses generational garbage collection to improve performance.
Objects are grouped into three generations based on how many collections they have survived.
|
Generation |
Description |
Collection frequency |
|---|---|---|
|
Gen 0 |
Newly created objects |
Most frequent |
|
Gen 1 |
Objects surviving Gen 0 collections |
Less frequent |
|
Gen 2 |
Long-lived objects |
Least frequent |
Example:
class Employee
{
public string Name { get; set; }
}
Employee emp = new Employee();
Console.WriteLine(GC.GetGeneration(emp));
Typical Output:
0
Explanation:
-
A newly created small object normally starts in Generation 0.
-
If it survives a Gen 0 collection, it may move to Generation 1.
-
If it survives further collections, it may eventually move to Generation 2.
Diagram:
New Object
|
v
Generation 0
|
| Survives collection
v
Generation 1
|
| Survives collection
v
Generation 2
Key Points:
-
Gen 0 contains short-lived objects.
-
Gen 1 acts as a buffer between short-lived and long-lived objects.
-
Gen 2 contains long-lived objects.
-
A Gen 2 collection also includes Gen 0 and Gen 1.
-
Large objects (normally 85,000 bytes or more) use the Large Object Heap, which is collected with Gen 2.
4. What Is the Difference Between Stack and Heap Memory?
The stack is primarily used for method execution and local storage, while the heap stores dynamically allocated objects.
|
Stack |
Heap |
|---|---|
|
Stores stack frames and certain local values |
Stores class instances and arrays |
|
Managed through method execution |
Managed through GC for managed objects |
|
Very fast allocation |
Allocation is generally fast but involves GC management |
|
Typically thread-specific |
Objects can be shared between threads |
|
Limited stack space |
Generally much larger memory area |
Example:
class Employee
{
public int Id;
}
void Test()
{
int age = 30;
Employee emp = new Employee();
emp.Id = 101;
}
Conceptual Memory Diagram:
STACK HEAP
------------------- -------------------
age = 30
emp (reference) ------------> Employee Object
Id = 101
------------------- -------------------
Explanation:
-
ageis a local value-type variable, typically stored in the stack frame. -
empis a local reference variable, typically stored in the stack frame. -
The
Employeeobject is allocated on the managed heap. -
The object's
Idfield is stored as part of that object.
Key Points:
-
Value types are not always stored on the stack.
-
Value-type fields inside class objects are stored within those heap objects.
-
Reference-type objects are normally allocated on the managed heap.
-
The stack is automatically unwound as methods return.
-
Heap memory is reclaimed by GC when objects become unreachable.
5. What Is the Difference Between Managed and Unmanaged Resources?
Managed resources are objects whose memory is managed by the .NET Garbage Collector.
Unmanaged resources are operating system or native resources that require explicit cleanup, either directly or through a managed wrapper.
|
Managed Resources |
Unmanaged Resources |
|---|---|
|
Memory managed by GC |
Not automatically released by GC |
|
Examples: C# objects, arrays |
Examples: native handles, native memory |
|
Memory reclaimed automatically |
Require explicit release |
|
Usually no manual memory cleanup |
Often handled through |
Example:
// Managed object
List<int> numbers = new List<int>();
// Managed wrapper around a file resource
using FileStream fs = new FileStream(
"test.txt",
FileMode.OpenOrCreate
);
Explanation:
-
List<int>is a managed object. -
FileStreamis also a managed object, but it wraps an operating system file handle. -
The
usingdeclaration ensuresDispose()is called when the variable goes out of scope. -
This releases the file resource promptly rather than waiting for garbage collection.
Key Points:
-
GC manages object memory, not the lifetime of all external resources.
-
File handles, socket handles, and native memory require proper cleanup.
-
IDisposableprovides a standard cleanup mechanism. -
Prefer
usingorawait usingfor disposable resources. -
A database connection object is managed, but it controls external resources that must be released promptly.
6. What Is the Difference Between Dispose() and a Finalizer?
Both are related to resource cleanup, but their execution and purpose are different.
Dispose() provides deterministic cleanup, whereas a finalizer provides a fallback cleanup mechanism for certain unmanaged resources.
|
|
Finalizer |
|---|---|
|
Called explicitly or through |
Invoked by the GC finalization mechanism |
|
Deterministic cleanup |
Non-deterministic cleanup |
|
Implements |
Uses |
|
Can release managed and unmanaged resources appropriately |
Primarily for direct unmanaged-resource cleanup |
|
Preferred approach |
Fallback mechanism |
Example:
class ResourceManager : IDisposable
{
public void Dispose()
{
Console.WriteLine("Dispose called");
GC.SuppressFinalize(this);
}
~ResourceManager()
{
Console.WriteLine("Finalizer called");
}
}
using (ResourceManager resource = new ResourceManager())
{
Console.WriteLine("Using resource");
}
Output:
Using resource
Dispose called
Explanation:
-
The object is created inside a
usingblock. -
At the end of the block,
Dispose()is called automatically. -
GC.SuppressFinalize(this)prevents finalization for this instance. -
The finalizer is not executed in this example.
-
The example demonstrates lifecycle behavior; a real implementation must actually release its owned resources.
Key Points:
-
Dispose()is used for immediate, predictable resource cleanup. -
A finalizer runs at an unspecified time and may never run before process termination.
-
Finalizers should generally be implemented only when directly owning unmanaged resources.
-
Prefer
SafeHandlerather than writing a custom finalizer. -
GC.SuppressFinalize(this)avoids unnecessary finalization after successful disposal. -
Dispose()does not directly free the managed object's heap memory; GC handles that later.
Interview Quick Revision
|
Question |
Important answer |
|---|---|
|
What is GC? |
Automatic managed memory reclamation |
|
How does GC work? |
Finds reachable objects and reclaims unreachable objects |
|
What are generations? |
Gen 0, Gen 1, and Gen 2 optimize collection |
|
Stack vs Heap? |
Stack supports method execution; heap stores allocated objects |
|
Managed vs Unmanaged? |
GC-managed memory vs resources needing explicit cleanup |
|
Dispose vs Finalizer? |
Deterministic cleanup vs non-deterministic fallback |
Important interview point: Garbage Collection and Dispose() solve different problems. GC reclaims managed memory, while Dispose() releases resources promptly.