Turn Your Vehicle Into a Smart Earning Asset

While you’re not driving your car or bike, it can still be working for you. MOTOSHARE helps you earn passive income by connecting your vehicle with trusted renters in your city.

🚗 You set the rental price
🔐 Secure bookings with verified renters
📍 Track your vehicle with GPS integration
💰 Start earning within 48 hours

Join as a Partner Today

It’s simple, safe, and rewarding. Your vehicle. Your rules. Your earnings.

DOTNET: Understanding Garbage Collection with Gen0, Gen1, Gen2

Yes, we can absolutely do this in one self-contained program 👍
You’ll be able to:

  • See GC before/after each scenario
  • Trigger Gen0 / Gen1 / Gen2 collections
  • Observe heap size and collection counts
  • Use this with dotnet-counters / dotMemory / VS profiler if you want

Below is a single Program.cs you can drop into a new console app.


1️⃣ Create the project

dotnet new console -n GcGenerationsDemo
cd GcGenerationsDemo
Code language: JavaScript (javascript)

Replace all contents of Program.cs with this:



2️⃣ How to run and “experience” GC

From inside the project folder:

dotnet run

The program will walk you through:

  1. Startup stats
  2. Gen0 scenario – short-lived allocations
  3. Gen1 scenario – medium-lived allocations
  4. Gen2 scenario – long-lived allocations

Each step prints:

  • Gen0/Gen1/Gen2 collection counts
  • Total managed heap (MB)

You’ll see:

  • Gen0 counts jump a lot in the first scenario.
  • Gen1 counts increase in the second scenario.
  • Gen2 counts and heap size behavior change in the third scenario.

3️⃣ Optional: Watch it live with dotnet-counters

In another terminal, while the app is running:

  1. Get process list: dotnet-counters ps
  2. Find your GcGenerationsDemo PID, then: dotnet-counters monitor --process-id <PID> System.Runtime

Watch:

  • gc-heap-size
  • gen-0-gc-count
  • gen-1-gc-count
  • gen-2-gc-count

Run each scenario and you’ll see the counters move in sync with console output.


4️⃣ How this maps to Gen0 / Gen1 / Gen2 concepts

  • Gen0 scenario
    • Many tiny, short-lived objects
    • Mostly collected in Gen0
    • You’ll see Gen0 collections spike
  • Gen1 scenario
    • Objects kept alive briefly in a List<byte[]>
    • They survive at least one collection → promoted to Gen1
    • We force Gen1 collections and then free references
    • You see Gen1 counts increase, heap shrink
  • Gen2 scenario
    • Objects stored in a static list (LongLivedHolder.Buffers)
    • They are long-lived; promoted to Gen2
    • Even after Gen2 collection, many remain because references are still held
    • This is how leaks and long-lived caches behave



1️⃣ How to read the four numbers

Each PrintGcStats gives you:

  • Gen0 collections – how many times .NET cleaned short-lived objects
  • Gen1 collections – how many times it cleaned objects that survived Gen0
  • Gen2 collections – how many times it cleaned long-lived objects
  • Total managed heap – how much managed memory is still in use after GC (roughly)

These counters are cumulative since process start.


2️⃣ Gen0 scenario – short-lived objects

You saw:

=== Gen0 Scenario: Short-lived objects ===
--- GC Stats: Before Gen0 work ---
  Gen0 collections: 0
  Gen1 collections: 0
  Gen2 collections: 0
  Total managed heap: 0 MB

Gen0 scenario completed in 35 ms
--- GC Stats: After Gen0 forced collection ---
  Gen0 collections: 71
  Gen1 collections: 1
  Gen2 collections: 0
  Total managed heap: 0 MB

What happened?

  • We allocated millions of tiny objects in a loop.
  • They were not stored anywhere, so they died quickly.
  • GC cleaned them mostly in Gen0:
    • Gen0 collections: 0 → 71
  • A few objects survived briefly → promoted to Gen1 once:
    • Gen1 collections: 0 → 1
  • After the forced collection:
    • Total managed heap: 0 MB (rounded) → means almost nothing left.

👉 Interpretation:

“Lots of short-lived garbage → GC handled it cheaply in Gen0.
We generated a ton of allocations, but the memory was fully reclaimed. No leak, GC working as designed.”

This is typical of request-scoped allocations in APIs when they’re well-behaved.


3️⃣ Gen1 scenario – medium-lived objects

You saw:

=== Gen1 Scenario: Medium-lived objects ===
--- GC Stats: Before Gen1 work ---
  Gen0 collections: 71
  Gen1 collections: 1
  Gen2 collections: 0
  Total managed heap: 0 MB

Gen1 scenario completed in 2613 ms
--- GC Stats: After Gen1 forced collections ---
  Gen0 collections: 627
  Gen1 collections: 296
  Gen2 collections: 33
  Total managed heap: 3131 MB

What did the code do here?

  • It allocated a lot of 16 KB buffers and stored them in a List<byte[]> survivors.
  • That local list stayed alive for a while → the buffers survived multiple Gen0 collections.
  • That caused:
    • Gen0 collections: 71 → 627 (lots of allocations)
    • Gen1 collections: 1 → 296 (many promotions & cleanups)
    • Gen2 collections: 0 → 33 (some long-lived promotions too)

Why is heap so big here (3131 MB)?

  • Right after the scenario, before the runtime has fully compacted and reused memory, GetTotalMemory sees ~3 GB still reserved/used.
  • These objects were just cleared at the end of the scenario (we call survivors.Clear() and GC.Collect), but this snapshot is still showing that a lot of memory was in play.

Then before the next scenario you saw:

--- GC Stats: Before Gen2 work ---
  ...
  Total managed heap: 2 MB

So eventually the runtime fully reclaimed it, and the heap dropped back down.

👉 Interpretation:

“Here we created objects that lived longer than Gen0 (in a list).
We see big jumps in Gen1 and Gen2 collections and temporary heap growth (~3 GB).
After clearing references and more GC, memory drops back to a few MB → no leak, just heavy temporary pressure.”

This demonstrates:

  • Promotion from Gen0 → Gen1 → Gen2
  • Longer-lived objects = more expensive GC
  • Why you don’t want to keep large collections alive longer than necessary.

4️⃣ Gen2 scenario – long-lived / leaked objects

You saw:

=== Gen2 Scenario: Long-lived objects ===
--- GC Stats: Before Gen2 work ---
  Gen0 collections: 627
  Gen1 collections: 296
  Gen2 collections: 33
  Total managed heap: 2 MB

Gen2 scenario completed in 184 ms
--- GC Stats: After Gen2 forced collection ---
  Gen0 collections: 693
  Gen1 collections: 329
  Gen2 collections: 34
  Total managed heap: 392 MB

Long-lived objects stored: 50000
Note: Because we still keep references, these objects won't be freed.

What did the code do here?

  • Allocated 50,000 × 8 KB buffers ≈ ~400 MB.
  • Stored them in LongLivedHolder.Buffers, which is static.
  • We never clear that list → those objects are effectively long-lived.

Even after a full Gen2 collection:

  • Gen2 collections: 33 → 34 (we forced a full GC)
  • But heap only drops to:
    • Total managed heap: 392 MB
  • And we still have:
    • Long-lived objects stored: 50000

👉 Interpretation:

“These are long-lived objects (or a leak).
Even after a full Gen2 GC, almost 400 MB remains because we’re still holding references in a static list.
This is what a memory leak / long-lived cache looks like in production:
Gen2 collections happen, but memory never really goes down.”

This is the pattern you’d see in:

  • Static caches that don’t evict
  • Static lists/dicts that only grow
  • Singletons holding on to big data
  • Event handler leaks, etc.

5️⃣ How to explain (short version)

“In the Gen0 scenario, we created millions of tiny, short-lived objects.
GC handled them mostly in Gen0 (71 collections), and after GC, memory is basically 0 MB.
This is healthy, short-lived garbage.

In the Gen1 scenario, we kept objects alive for a while in a list.
They survived Gen0, got promoted to Gen1 and some to Gen2.
We see big Gen1/Gen2 collection counts and temporary heap growth to ~3 GB, but after clearing references, the heap returns to a small size.
This shows the cost of medium-lived objects.

In the Gen2 scenario, we stored 50k buffers in a static list.
Even after a full Gen2 collection, we still use ~392 MB.
Because the app still references these objects, the GC cannot free them.
This is exactly how long-lived objects and memory leaks behave in real .NET apps.”

Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments

Certification Courses

DevOpsSchool has introduced a series of professional certification courses designed to enhance your skills and expertise in cutting-edge technologies and methodologies. Whether you are aiming to excel in development, security, or operations, these certifications provide a comprehensive learning experience. Explore the following programs:

DevOps Certification, SRE Certification, and DevSecOps Certification by DevOpsSchool

Explore our DevOps Certification, SRE Certification, and DevSecOps Certification programs at DevOpsSchool. Gain the expertise needed to excel in your career with hands-on training and globally recognized certifications.

0
Would love your thoughts, please comment.x
()
x