Skip to content

Instantly share code, notes, and snippets.

@devops-school
Created November 26, 2025 07:15
Show Gist options
  • Select an option

  • Save devops-school/99d99d45e2aa02fc8b64427fe4ad4182 to your computer and use it in GitHub Desktop.

Select an option

Save devops-school/99d99d45e2aa02fc8b64427fe4ad4182 to your computer and use it in GitHub Desktop.
DOTNET: Understanding Garbage Collection with Gen0, Gen1, Gen2
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=========================================");
Console.WriteLine(" .NET GC Generations Demo (Gen0/1/2) ");
Console.WriteLine("=========================================");
Console.WriteLine($"Is Server GC: {System.Runtime.GCSettings.IsServerGC}");
Console.WriteLine();
PrintGcStats("Startup");
Pause("Press ENTER to run Gen0 (short-lived objects) scenario...");
RunGen0Scenario();
Pause("Press ENTER to run Gen1 (mid-lived objects) scenario...");
RunGen1Scenario();
Pause("Press ENTER to run Gen2 (long-lived objects) scenario...");
RunGen2Scenario();
Pause("Done. Press ENTER to exit.");
}
// -----------------------------
// Scenario 1: Gen0 focus
// Lots of short-lived objects that die quickly
// -----------------------------
static void RunGen0Scenario()
{
Console.WriteLine();
Console.WriteLine("=== Gen0 Scenario: Short-lived objects ===");
PrintGcStats("Before Gen0 work");
var sw = Stopwatch.StartNew();
// Allocate many small, short-lived objects
for (int i = 0; i < 5_000_000; i++)
{
// Each allocation is ~64 bytes
var bytes = new byte[64];
// We don't store 'bytes' anywhere -> eligible for Gen0 collection
bytes[0] = 1;
}
// Force a Gen0 collection only (ephemeral)
GC.Collect(0, GCCollectionMode.Forced, blocking: true, compacting: false);
sw.Stop();
Console.WriteLine($"Gen0 scenario completed in {sw.ElapsedMilliseconds} ms");
PrintGcStats("After Gen0 forced collection");
}
// -----------------------------
// Scenario 2: Gen1 focus
// Objects survive a bit (promoted), then are freed
// -----------------------------
static void RunGen1Scenario()
{
Console.WriteLine();
Console.WriteLine("=== Gen1 Scenario: Medium-lived objects ===");
PrintGcStats("Before Gen1 work");
var sw = Stopwatch.StartNew();
// These objects will be kept alive through at least one Gen0 GC
var survivors = new List<byte[]>();
for (int i = 0; i < 200_000; i++)
{
// Each ~16 KB, total ~3.2 GB allocated over time (but reused by GC)
var buffer = new byte[16 * 1024];
buffer[0] = 1;
buffer[buffer.Length - 1] = 2;
survivors.Add(buffer);
}
// Force a Gen1 collection (which also collects Gen0)
GC.Collect(1, GCCollectionMode.Forced, blocking: true, compacting: false);
// Now clear references so they become collectible
survivors.Clear();
// Another collection so they actually disappear
GC.Collect(1, GCCollectionMode.Forced, blocking: true, compacting: false);
sw.Stop();
Console.WriteLine($"Gen1 scenario completed in {sw.ElapsedMilliseconds} ms");
PrintGcStats("After Gen1 forced collections");
}
// -----------------------------
// Scenario 3: Gen2 focus
// Long-lived objects (static / global) promoted and retained
// -----------------------------
static void RunGen2Scenario()
{
Console.WriteLine();
Console.WriteLine("=== Gen2 Scenario: Long-lived objects ===");
PrintGcStats("Before Gen2 work");
var sw = Stopwatch.StartNew();
// These allocations are stored in a static holder and never cleared
for (int i = 0; i < 50_000; i++)
{
// Each ~8 KB -> total ~400 MB if all kept
var buffer = new byte[8 * 1024];
buffer[0] = 1;
buffer[buffer.Length - 1] = 2;
LongLivedHolder.Buffers.Add(buffer);
}
// Force a full Gen2 collection (collects Gen0, Gen1, Gen2)
GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true);
sw.Stop();
Console.WriteLine($"Gen2 scenario completed in {sw.ElapsedMilliseconds} ms");
PrintGcStats("After Gen2 forced collection");
Console.WriteLine($"Long-lived objects stored: {LongLivedHolder.Buffers.Count}");
Console.WriteLine("Note: Because we still keep references, these objects won't be freed.");
}
// -----------------------------
// Helpers
// -----------------------------
static void PrintGcStats(string label)
{
long gen0 = GC.CollectionCount(0);
long gen1 = GC.CollectionCount(1);
long gen2 = GC.CollectionCount(2);
long totalBytes = GC.GetTotalMemory(forceFullCollection: false);
Console.WriteLine($"--- GC Stats: {label} ---");
Console.WriteLine($" Gen0 collections: {gen0}");
Console.WriteLine($" Gen1 collections: {gen1}");
Console.WriteLine($" Gen2 collections: {gen2}");
Console.WriteLine($" Total managed heap: {totalBytes / (1024 * 1024)} MB");
Console.WriteLine();
}
static void Pause(string message)
{
Console.WriteLine();
Console.WriteLine(message);
Console.ReadLine();
}
}
// Static holder to simulate Gen2 / long-lived objects
static class LongLivedHolder
{
public static readonly List<byte[]> Buffers = new();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment