Published on

Garbage Collection: .NET vs Go

Authors

Both .NET and Go employ automatic memory management through garbage collection (GC), but their approaches differ significantly.

.NET Garbage Collection

.NET uses a generational, mark-and-compact collector:

Generational Garbage Collection in .NET

.NET's garbage collector is based on the generational hypothesis, which states that most objects have short lifetimes. The GC divides objects into three generations:

  1. Generation 0 (Gen 0):
    • The youngest generation.
    • All new objects start here.
    • Collected most frequently.
    • Very fast and efficient for short-lived objects.
  2. Generation 1 (Gen 1):
    • Objects that survive a Gen 0 collection are promoted here.
    • Serves as a buffer between short-lived and long-lived objects.
    • Collected less frequently than Gen 0.
  3. Generation 2 (Gen 2):
    • The oldest generation.
    • Contains long-lived objects.
    • Collected least frequently.
    • Full collection of Gen 2 is the most expensive operation.

How it works:

  • When Gen 0 fills up, a garbage collection is triggered.
  • If objects in Gen 0 survive, they're moved to Gen 1.
  • If Gen 1 fills up, both Gen 0 and Gen 1 are collected.
  • Objects surviving from Gen 1 are moved to Gen 2.
  • When Gen 2 fills up, a full garbage collection occurs (all generations).

Advantages of Generational GC:

  • Efficient for applications with many short-lived objects.
  • Reduces the work done in most collections by focusing on younger generations.
  • Allows for different collection strategies for different generations.

Additional .NET GC Features:

  • Mark-and-Compact: Live objects are marked and compacted to reduce fragmentation.
  • Concurrent: Recent versions support background GC to minimize pauses.
  • Workstation vs. Server: Different GC strategies for different application types.

.NET's GC is highly tunable, with options for concurrent, background, and incremental collection.

Go Garbage Collection

Go employs a concurrent, tri-color mark-and-sweep collector:

  • Concurrent: GC runs concurrently with the application, reducing pause times.
  • Non-generational: All objects are treated equally, regardless of age.
  • Mark-and-Sweep: Live objects are marked; unmarked objects are swept (freed).
  • Write Barrier: Ensures consistency during concurrent collection.

Go's GC prioritizes low latency over maximum throughput, aiming for sub-millisecond pause times.

Key Differences

  1. Generational Approach: .NET uses generations, Go doesn't.
  2. Compaction: .NET compacts memory; Go doesn't, potentially leading to fragmentation.
  3. Tuning: .NET offers more tuning options; Go's GC is simpler with fewer knobs.
  4. Pause Times: Go generally achieves shorter pause times, crucial for certain applications.
  5. Memory Overhead: .NET's GC can use more memory due to its generational nature.

Both systems continue to evolve, with ongoing research and improvements focused on reducing pause times and improving overall performance.

Comparison

Feature.NETGo
Collection TypeGenerational Mark-and-CompactNon-generational Mark-and-Sweep
ConcurrencyConcurrent and Background GCFully Concurrent
Generations3 (Gen 0, 1, 2)None
Memory CompactionYesNo
Tuning OptionsManyFew
Pause TimesCan be longer, especially for Gen 2Generally shorter (sub-millisecond)
Memory OverheadHigher due to generationsLower
FragmentationLess likely due to compactionMore likely
Primary OptimizationThroughputLow latency
Workstation vs ServerDifferent strategies availableSingle strategy for all deployments