RunMat
GitHub

Memory Management

runmat-gc provides garbage-collected storage for runtime values that need stable identity or shared ownership across the VM and runtime. It is used for cell contents, handle-object targets, listener callbacks, selected struct/object payloads, and JIT/runtime bridge values.

For the broader runtime value model, see Runtime Values & Type Model. This page focuses on the subset of values that need GC-managed identity or reachability.

The collector is non-moving. A GcPtr<Value> is an address-stable pointer to a Value allocated by the GC. Collection marks reachable values from registered roots, drops unreachable values in place, and keeps the outer pointer identity stable for surviving objects.

Runtime Shape

Loading diagram...

Managed Values

The GC manages the outer Value allocation. Nested payloads such as Vec, String, tensor buffers, and struct maps remain owned by Rust values inside that allocation and are released through normal destructors when the GC drops the outer Value.

Value shapeGC role
CellArrayStores Vec<GcPtr<Value>>, so each cell element can be shared and independently rooted.
HandleObjectStores a GcPtr<Value> target to preserve handle identity.
ListenerStores GcPtr<Value> references to the target and callback.
Object and Struct payloadsCan be placed behind a GC pointer when identity or sharing is required.
GpuTensorRegisters a finalizer so provider-owned GPU buffers are freed when the GC value is collected.

Plain numeric arrays, strings, logical arrays, and tensors are not deep-managed by the GC. Their heap buffers are owned by Rust allocation inside the stored Value.

Allocation

All new GC values are allocated in generation 0. The allocator groups memory into size classes and writes the Value into an address recorded by the young generation.

ComponentRole
GenerationalAllocatorOwns generation blocks and allocation cursors.
GenerationTracks size-class blocks, allocated bytes, allocation starts, and survivor state.
SizeClassSelects small, medium, or large allocation blocks.
GcStatsRecords allocation count, allocated bytes, current memory, and peak memory.

The current size estimate is the size of the outer Value. This avoids over-reserving for nested Rust-owned payloads and keeps GC accounting aligned with what the GC allocator directly owns.

Collection

Collection is mark-and-sweep over the GC-managed Value headers.

Loading diagram...

Minor collection is triggered when the young generation exceeds minor_gc_threshold; aggressive low-threshold configurations also collect periodically by allocation count. gc_collect_minor() can force the same path.

Major collection uses the full-root collection entrypoint, clears remembered-set barriers, records a major collection event, and resets survivor counters used for generational policy. In the current implementation, sweeping is still centered on tracked young allocations; promoted objects are logical generation state, not a separately compacted old space.

The collector does not compact surviving objects or move them between physical blocks. Promotion is logical: after a value survives enough collections, the allocator treats its address as older for barrier decisions.

Roots

Roots are the entry points that keep GC values alive.

Root sourceCode entityPurpose
Explicit rootsgc_add_root / gc_remove_rootProtect a specific GcPtr<Value> by address.
VM stackStackRootScans the interpreter stack for nested GC pointers.
VM variablesVariableArrayRootScans the interpreter variable array.
Global valuesGlobalRootKeeps runtime/global values reachable during execution.
Registered rootsRootScannerStores root objects by RootId and removes inactive roots.
Remembered setWriteBarrierManagerAdds old-to-young references as minor-GC roots.

runmat-vm wraps interpretation in an InterpretContext. The context registers stack and variable-array roots on creation and unregisters them on drop. Additional global roots can be registered for values that must stay live during an interpreter call.

Write Barriers

Generational collection must preserve references from older objects to younger objects. RunMat records those edges through gc_record_write(old, new).

The barrier path checks each value's logical generation. If the old value is older than the new value, the old address is inserted into the remembered set. Minor GC then treats remembered-set entries as additional roots.

Write barriers are used in mutation paths such as cell assignment, object property writes, and indexing writes that can install a new GC-managed value into an existing aggregate.

Configuration

GC configuration can be set through runtime config, CLI flags, or direct API calls.

Config fieldMeaning
presetSelects low-latency, high-throughput, low-memory, or debug.
young_size_mbOverrides generation 0 size from runtime config or CLI.
threadsSets max_gc_threads in the GC config.
collect_statsEnables statistics collection for reporting.

The lower-level GcConfig also includes thresholds, promotion policy, heap sizing, write-barrier settings, pointer-compression flag, logging, and collection timeouts.

PresetIntended behavior
default2 MB young generation, 80% minor threshold, 90% major threshold, three generations.
low-latencyCollects earlier and promotes after fewer survivals.
high-throughputUses a larger young generation and later thresholds.
low-memoryUses a smaller young generation and a bounded heap target.
debugEnables verbose logging and more frequent collection.

CLI commands expose the same control surface:

runmat gc stats
runmat gc minor
runmat gc major
runmat gc config
runmat gc stress --allocations 10000

Runtime startup maps --gc-preset, --gc-young-size, --gc-threads, and --gc-stats into runmat_gc::GcConfig before calling gc_configure.

Statistics

GcStats records allocation and collection counters for diagnostics and tuning.

MetricMeaning
total_allocations, total_allocated_bytesAggregate allocation volume.
minor_collections, major_collectionsCollection counts by type.
minor_collection_time, major_collection_timeTime spent collecting.
minor_objects_collected, major_objects_collectedObjects dropped by collection type.
objects_promotedLogical promotions after survival thresholds.
current_memory_usage, peak_memory_usageGC-accounted memory usage.
Recent collection historyBounded history of collection events.

The CLI and session APIs use the same stats object. summary_report() formats allocations, current and peak memory, collection frequency, average collection time, GC overhead, allocation rate, and memory utilization.

Boundaries

The GC is responsible for GcPtr<Value> lifetimes, not every byte used by the runtime. It does not replace Rust ownership for ordinary tensors, strings, vectors, or host-side buffers. GPU buffers are provider-owned and are released through registered finalizers when a GC-managed Value::GpuTensor is collected.

This design keeps MATLAB identity-bearing values stable while letting Rust continue to own the bulk storage for ordinary value payloads.