January 31, 2026
Mark it, then yeet it
Writing a .NET Garbage Collector in C# – Part 6: Mark and Sweep
Teaching apps to take out the trash — and the comments are on fire
TLDR: The latest chapter shows how .NET’s garbage collector marks which data is still in use before cleanup. Comments erupted into praise for the deep dive, snarky “just use Rust” takes, and debates over a quirky callback setup—making a niche memory lesson feel surprisingly vital for everyday software reliability.
After a long break, the author is back teaching how to build a garbage collector (GC), the part of .NET that cleans up unused stuff in memory. This chapter shows the “mark” step: starting from roots (things the program always keeps), walking through variables via a callback, and tagging what’s still alive. The blog explains how they pass a little “context” object into the runtime so a static callback can find the right heap instance, and why some settings (like “promotion”) are set for scanning. If you’re curious, the author also points readers to the second edition of Pro .NET Memory Management for deeper internals.
The comments? Theater. One camp cheers a hands‑on tour of how memory really works, dubbing it “CS textbook material.” The other fires hot takes: “Why write a GC in C#—just use Rust,” and “UnmanagedCallersOnly is summoning native demons.” Memes fly: “Mark and Sweep = chores,” “Mark‑and‑Yeet,” plus a raccoon gif taking out trash. Pragmatists grumble the break was “way too long,” while veterans insist understanding roots and callbacks prevents leaks. The mini‑war: is stuffing a pointer in the context clever engineering or cursed hackery? Then the author teases “cons…,” and bets land on conservative mode.
Key Points
- •The article implements the mark phase of a .NET mark-and-sweep garbage collector in C#.
- •GC roots are categorized into local/thread-static storage, GC handles, and the finalization queue; statics are kept alive via GC handles.
- •Local roots are reported by the runtime using IGCToCLR.GcScanRoots, which takes a callback and a ScanContext.
- •ScanContext’s promotion flag is set to true, and its _unused1 field stores a GCHeap reference via GCHandle for callback access.
- •The root-scanning callback must be static and marked UnmanagedCallersOnly; it retrieves GCHeap from ScanContext and forwards scanning.