Objects on the heap form a directed reference graph G = (V, E): an edge o → p means field/variable o holds a pointer to p. A fixed set of roots (stack variables, globals — shown gold) anchors the graph. An object is live iff it is reachable from some root:
Live(o) = true iff ∃ path root ⇒ o in G
Mark phase (BFS from roots), O(|V| + |E|):
R ← roots ; visited ← {}
while R not empty:
o ← R.pop(); if o in visited: continue
visited.add(o); mark(o) = true
for each edge o → p: R.push(p)
Sweep phase, O(|V|):
for every object o in heap:
if mark(o) == false: free(o) // unreachable ⇒ garbage
else: mark(o) ← false // reset for next cycle
Anything not visited during the mark BFS has no path from a root — even if other garbage objects still point to it — so the whole disconnected component is reclaimed together. That's why dropping one reference can free a cluster of objects at once, not just the one whose pointer was cleared.
Generational mode exploits the weak generational hypothesis — most objects die young. New objects start in the young generation (green). A minor GC only traces roots and young objects, treating every old-generation object as automatically alive (approximating a remembered set of old→young pointers) — so it's much cheaper than scanning the whole heap. An object that survives two minor collections is promoted to the old generation (purple). A Full GC traces the entire graph, old generation included, and is needed to collect old objects that have genuinely become garbage.
Node positions are computed with a force-directed layout, the same technique used by real graph-visualization and heap-inspector tools: a Coulomb-like repulsion keeps every pair of objects apart, a Hookean spring pulls referenced objects together, and a weak centering force keeps the whole graph in frame.
F_repel(i,j) = k_r / d(i,j)² (push apart, all pairs)
F_spring(i,j) = k_s · (d(i,j) − L0) (pull together, only for i→j edges)
F_center(i) = −k_c · pos(i) (weak pull to origin)
v ← (v + F·dt) · damping ; pos ← pos + v·dt
- Allocate Object — mallocs a new node with 0–2 random outgoing references, wired into the live graph.
- Add / Drop Reference — the mutator rewiring a pointer; dropping the last reference into a subgraph turns it into garbage instantly, even though nothing is deleted until the collector runs.
- Run Mark & Sweep — animates the BFS wavefront (blue pulse spreading from the gold roots), then shrinks and ejects every node the wavefront never reached.
- Auto-collect — triggers a cycle automatically once heap usage crosses the threshold, the way a real allocator schedules collections under memory pressure.