9.5 Garbage Collection, Reference Counting, and Ownership
Memory management is a runtime policy, but compiler design heavily influences how well that policy performs and how safe programs remain.
Three major styles appear frequently:
- tracing GC (for example mark-sweep and descendants)
- reference counting
- ownership/borrow systems with compile-time constraints
No single strategy dominates all workloads. The right choice depends on latency goals, throughput goals, interoperability constraints, and language semantics.
Tracing vs Reference Counting
Tracing collectors reason from roots and reachability. This naturally handles cyclic structures.
Reference counting reclaims promptly when counts drop to zero, often helping local latency, but pure RC misses cycles unless supplemented.
From compiler/runtime co-design perspective, tradeoffs include:
- pause behavior
- write-barrier or counter-update overhead
- metadata layout
- foreign-resource integration
Ownership as Static Memory Discipline
Ownership/borrow models move many memory-safety checks to compile time.
A common rule is "many readers or one mutable writer" at any point in time. This constrains dangerous aliasing patterns before code runs.
Benefits include predictable destruction and strong use-after-free prevention. Costs include stricter API design and explicit lifetime modeling.
In many modern systems, practical architectures blend strategies: ownership for deterministic resources, plus runtime GC for managed object graphs, or RC plus cycle detection.
Worked Decision Lens
When evaluating a runtime policy, ask:
- what is the latency budget?
- what object graph patterns dominate?
- what interoperability model is required?
- where do we want errors: compile time, runtime, or both?
These are language-and-platform engineering decisions, not purely theoretical ones.