Automatic garbage collection
ManagedHeap is the public allocation boundary. It owns objects, arrays, VM closures, and VM capture cells; RuntimeValue stores typed non-owning references to those nodes. Collection occurs only at interpreter allocation safepoints or through collectGarbage().
Roots include globals, module environments, active lexical environments, VM registers, closures, capture cells, bound receivers, class state, and module namespaces. Marking uses an iterative worklist, so deep object/array/closure graphs cannot overflow the C++ stack. Because managed object edges are non-owning, unreachable cycles—including recursive closures—are reclaimed.
The growth policy uses live heap size rather than lifetime allocation count. Closure allocation is an explicit VM safepoint: active registers, boxed registers, and frame captures form a typed root set before collection. C++ HeapStats reports live, allocated, reclaimed, collection count, peak live size, the next threshold, and live counts for objects, arrays, capture cells, closures, bound methods, and errors.
Bytecode execution returns the same statistics for embedding and tests. kyna run --heap-stats prints the complete snapshot. Kyna's existing gcStats() string and collectGarbage() behavior remain source-compatible while exposing the richer class counts.
The runtime suite retains and then releases a 8,001-node cyclic object/array graph. This exercises iterative marking, verifies exact per-class counts while rooted, and proves every node is reclaimed after the temporary root scope ends.
Source captured: 2026-10-11