| Crates.io | runmat-gc |
| lib.rs | runmat-gc |
| version | 0.2.8 |
| created_at | 2025-10-15 04:18:49.998017+00 |
| updated_at | 2025-12-22 21:36:32.677179+00 |
| description | Generational garbage collector for RunMat with optional pointer compression |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1883744 |
| size | 222,093 |
runmat-gc implements a production-oriented, generational mark-and-sweep garbage collector for RunMat. The design prioritizes correctness and predictable semantics for a dynamic language runtime, while keeping performance and simplicity at the forefront. It exposes a stable, handle-based API via GcPtr<T> to avoid raw-pointer hazards and to keep integration with the interpreter straightforward.
Key properties:
Non-moving, generational allocation by default (young/old generations)
Mark-and-sweep collection with promotion based on survival counts
Explicit root management (stack, globals, persistents, test roots)
Write barrier and remembered-set tracking for old→young references
Centralized statistics (GcStats) and configurable policies (GcConfig)
Stable handle type (GcPtr<Value>) re-exported from runmat-gc-api
The GC is composed of the following subsystems:
HighPerformanceGC: Top-level façade that unifies configuration, allocator, collector, roots, and write-barrier state. Provides public API (allocate, collect, stats, configure, root ops).
GenerationalAllocator: Owns heap memory across generations. Tracks young allocations, survivors, and promotion thresholds. Supports fast young-generation allocation and statistics query.
MarkSweepCollector: Implements the marking from explicit roots and sweeping of unmarked objects, including promotion decisions for survivors.
RootScanner and Root Registry: Centralizes explicit roots registered at runtime (e.g., VM stack, globals, persistents, test harness roots). During a collection, the collector queries the root registry to seed the mark phase.
WriteBarrierManager + CardTable: Records old→young references at mutation sites so that minor collections can scan remembered-set entries instead of all old-gen objects.
Stats and Configuration: GcStats exposes running counters (allocations, collections, promotions,
etc.). GcConfig governs thresholds (minor trigger ratio, young generation sizing, and more).
GcPtr)GcPtr<T> is a thin, stable handle to GC-managed objects. In the current implementation:
GcPtr<T> implements Deref/DerefMut to provide &T/&mut T. The only unsafe required by
the system lives inside those trait impls; user code should not write any unsafe for dereferencing.GcPtr<T> is Copy + Clone-like via a trivial bitwise copy (we implement Clone), so APIs that need
to retain a handle should explicitly clone it when passing ownership to a registry. Tests were updated
to clone when adding/removing roots to avoid moves.Note on moving/compaction: If a future configuration introduces object moving/compaction, we will add a
forwarding/indirection layer so that existing GcPtr values remain valid without changing embeddings.
The collector discovers live objects by scanning registered roots. The system supports:
Value slots; a VM-specific adapter
registers these with the root scanner for the duration of interpretation.global and persistent variables are
registered as global roots for the lifetime of the VM.gc_add_root/gc_remove_root to pin values across collections.During collection, the GC merges explicit roots with remembered-set derived minor roots.
A write barrier must run whenever an old-generation object gets a reference to a young-generation object.
The VM calls gc_record_write(old, new) at mutation sites (e.g., cell element writes, struct field
updates, object property updates). The barrier system stores card/slot metadata in WriteBarrierManager
so that minor collections only scan a compact set of remembered locations rather than the entire old gen.
Collections come in two flavors:
Minor (young-only):
Major (all generations): Mark & sweep across all generations; clears remembered-set state.
The allocator tracks survivor counts and performs promotion once an object survives a configurable
number of minor GCs. Promotion stats are reported via GcStats to tune policies.
GcConfig parameters:
young_generation_size: target size for young generation (bytes)minor_gc_threshold: utilization ratio to trigger a minor GCGcStats counters:
total_allocations, minor_collections, major_collections
objects_promoted, collections_performed, total_objects_collected
Other allocator/collector internal counters may be surfaced as needed
gc_allocate(value: Value) -> Result<GcPtr<Value>>gc_collect_minor(), gc_collect_major()gc_add_root(handle: GcPtr<Value>), gc_remove_root(handle: GcPtr<Value>)gc_configure(config: GcConfig), gc_get_config()gc_stats() -> GcStatsgc_record_write(old: &Value, new: &Value)Example (pinning a value across collections):
use runmat_builtins::Value;
use runmat_gc::*;
let v = gc_allocate(Value::Num(42.0)).unwrap();
gc_add_root(v.clone()).unwrap(); // pin
let _ = gc_collect_minor().unwrap(); // safe; v is alive
assert_eq!(*v, Value::Num(42.0));
gc_remove_root(v).unwrap(); // unpin when done
unsafe lives inside GcPtr<T> deref implementations. All external usage goes through
safe APIs.GcPtr addresses stable. The VM and runtime can store them without fear
of relocation.WriteBarrierManager,
root registry) is synchronized and marked Send/Sync where appropriate.Cell element writes
Struct field writes
Object property writes
The repository includes a broad test suite:
All tests can be run single-threaded for deterministic behavior:
cargo test --workspace -- --test-threads=1
The VM uses GcPtr<Value> throughout aggregates (e.g., CellArray stores Vec<GcPtr<Value>>).
All expansion and slice-assignment paths dereference GcPtr to read Value and write through handles
on mutation, with barrier calls at each write site.
The ignition runtime registers long-lived maps (globals/persistents) as roots for the duration of execution.
Implemented:
Generational allocator with promotion accounting
Mark-and-sweep collector over young/all generations
Root scanner and explicit root API
Write barriers and remembered set for old→young edges
Stable handle type re-exported from runmat-gc-api
Integration in the interpreter and runtime, with passing test suites
The following items are not required for correctness today but are desirable for performance, observability, and long-run robustness:
Barrier coverage audit and dedicated remembered-set test harness
Promotion policy tuning and survival-count decay after promotion
Extended telemetry: per-collection pause time, freed bytes, RS size, survivor/tenured histograms
Optional compaction (semi-space for young, sliding compaction for old) to reduce fragmentation
Fuzzing harness combining deep recursion, randomized allocation patterns, and mixed update/write paths
API polish: consider gc_add_root_ref(&GcPtr<Value>) to reduce accidental moves in user code
Configurable card sizes / RS structures for high-churn workloads
Contributions that improve performance, observability, or add new tests are welcome. Please keep changes focused and include benchmarks or tests where applicable.