Visit Sponsor

Written by 2:00 pm Blog

How Reference Counting Garbage Collection Works – Explained

Reference counting is one of the earliest and most intuitive garbage collection strategies used in memory management systems. It determines whether an object is reachable at runtime by keeping a counter of how many active references point to it. When this reference count drops to zero, the object is considered unreachable and can be collected.

This guide on javatechig.com explains how reference counting garbage collection works, its implementation mechanics, advantages, challenges (especially cycle detection), and how it compares with tracing GC algorithms used in systems like the JVM.

What Is Reference Counting in Garbage Collection?

Reference counting is a form of automatic memory management where each object maintains a counter representing the number of references pointing to it.

  • When a new reference to the object is created, the counter increments
  • When a reference is removed or goes out of scope, the counter decrements
  • When the counter reaches zero, the object can be safely reclaimed

This simple rule makes the algorithm easy to understand and implement.

How Reference Counting Works – Internals

Object Creation

When an object is created:

refCount = 0

Until references are assigned, the count starts at zero.

Reference Assignment

When a variable points to an object:

objA = new Object()
objB = objA
  • objA reference → refCount becomes 1
  • objB reference → refCount becomes 2

The collection system tracks these assignments to update counters.

Reference Removal

Reference removal happens when:

  • Variables go out of scope
  • Assignments change to point elsewhere
  • Data structures release references

Example:

objB = null

RefCount decrements to 1. The object is still reachable through objA.

Reaching Zero

When no references point to the object:

objA = null

Now:

refCount = 0
→ eligible for collection

Memory can be reclaimed immediately.

Example Walkthrough (Pseudo Code)

A = new Obj()     // A.refCount = 1
B = A             // A.refCount = 2
C = B             // A.refCount = 3

B = null          // A.refCount = 2
C = null          // A.refCount = 1
A = null          // A.refCount = 0 → free

This illustrates how incremental counting tracks object reachability without scanning the entire heap.

Benefits of Reference Counting

1. Immediate Reclamation

Objects are reclaimed as soon as they become unreachable — no need for periodic full heaps scans.

2. Predictable Behavior

Counters are updated during normal reference operations, making GC pauses very short.

3. Low Pause Latency

Unlike tracing GC (which pauses execution for mark/sweep), reference counting works incrementally, minimizing latency spikes.

Key Limitations

Cyclic References

The biggest weakness of reference counting is cycles:

A → B
B → A

Even if no outside references point to A or B, both refCounts remain non‑zero due to mutual references. This causes memory leaks, as neither object is reclaimed.

Overhead of Reference Updates

Every assignment or scope change involving references updates counters. In languages with frequent pointer operations, this overhead can impact performance.

Cycle Detection & Solutions

To address cycles, simple reference counting must be augmented with additional mechanisms:

1. Deferred Cycle Collection

Algorithms periodically scan objects to detect cycles using graph algorithms.

2. Hybrid Tracing + Counting

Combine reference counting with a tracing collector for cyclic garbage.

3. Weak References

In systems like Java, weak/soft/phantom references help break cycles or allow resurrection detection without interfering with normal reachability.

These augmentations make the scheme more robust but also more complex.

Reference Counting vs Tracing GC

PropertyReference CountingTracing (Mark & Sweep)
Immediate Reclaim⛔ (reclaims later)
Cycle Handling
Pause BehaviorVery SmallCan be Significant
Implementation CostModerateHigher (heap scanning)
Common UsageCOM, ObjC (retain/release)JVM, .NET, modern runtimes

Modern tracing collectors (e.g., G1 GC, ZGC) used in JVM don’t rely on simple reference counting because of cycle issues and performance tradeoffs.

Languages/Platforms That Use Reference Counting

Objective‑C

Objective‑C’s retain/release model used reference counting with cycle breakers like weak references.

Swift

Swift’s ARC (Automatic Reference Counting) extends basic reference counting with runtime cycle detection using weak/unowned references.

Python (CPython)

CPython uses reference counting as the primary GC with an auxiliary cycle detector.

Modern Hybrid GC

Many modern runtimes use hybrid approaches:

  • Reference counting for short‑lived objects
  • Tracing collectors for cycles
  • Write barriers and card tables for efficient cross‑generation pointers

Hybrid GC combines low latency with robust cycle collection.

When to Prefer Reference Counting

Reference counting may be preferred when:

✔ Low pause latency is critical
✔ Predictable incremental cleanup is desired
✔ Cycles are rare or broken by design
✔ Runtime overhead is acceptable

However, for complex apps with high object churn and cyclic references, tracing GC is usually a better fit.

Reference Counting in Modern JVM

The JVM does not use pure reference counting because of cyclic structures and performance scaling challenges. Instead, it relies on generational, tracing garbage collectors like:

  • G1 GC (default for most server workloads)
  • ZGC / Shenandoah for ultra‑low pause
  • Parallel GC for throughput

These algorithms scan live object graphs instead of just relying on counters.

Best Practices for Developers

✔ Understand your language’s GC model
✔ Avoid large cyclic data structures where possible
✔ Use weak/soft references where supported
✔ Profile memory with tools (e.g., VisualVM, Instruments, Python gc module)
✔ Monitor reference churn in performance‑sensitive code

Summary

Reference counting is a foundational memory management strategy that tracks object reachability by maintaining a counter of references. While simple and low‑latency, it struggles with cyclic references and high reference churn. Modern runtimes overcome these limitations with hybrid models that blend counting with tracing techniques, leading to more resilient and scalable garbage collection.

Visited 5 times, 2 visit(s) today
Close