memory management

Rava2 implements a comprehensive memory management system with automatic garbage collection, safety-focused allocation, and efficient data structures.

allocation safety suite

All memory allocations use wrapped functions that provide safety guarantees:

overflow protection

RAVA_CHECK_OVERFLOW_MUL(count, size)
RAVA_CHECK_OVERFLOW_ADD(a, b)

These macros validate that arithmetic operations will not overflow before performing allocations. If overflow would occur, the allocation fails safely.

allocation functions

functiondescription
rava_malloc(size)Allocates memory with NULL check
rava_calloc(count, size)Allocates zeroed memory with overflow check
rava_realloc(ptr, size)Reallocates memory with NULL check
RAVA_FREE(ptr)Frees memory and sets pointer to NULL

pointer nullification

The RAVA_FREE macro automatically sets pointers to NULL after freeing, preventing use-after-free bugs:

RAVA_FREE(obj);  // obj is now NULL

garbage collector

Rava2 uses a tricolor mark-and-sweep garbage collector for automatic memory management of Java objects.

tricolor marking algorithm

colormeaning
WHITEUnmarked - potentially garbage
GRAYMarked but children not yet scanned
BLACKMarked and all children scanned

The algorithm proceeds in three phases:

  1. Mark roots: All root objects (stack frames, globals) are marked gray
  2. Trace: Gray objects are processed, marking their references and turning black
  3. Sweep: All white objects are freed

object types

The GC manages 11 object types:

typedescription
OBJ_STRINGString instances
OBJ_ARRAYArray instances
OBJ_INSTANCEClass instances
OBJ_CLASSClass metadata
OBJ_METHODMethod definitions
OBJ_NATIVE_METHODNative method bindings
OBJ_CLOSUREClosures with captured variables
OBJ_UPVALUECaptured variables
OBJ_LAMBDALambda expressions
OBJ_STREAMStream objects
OBJ_METHOD_REFMethod references

object flags

flagdescription
OBJ_FLAG_MARKEDObject is reachable
OBJ_FLAG_FINALIZEDFinalizer has been called

heap management

block-based allocation

The heap is organized as a linked list of HeapBlock structures:

  • Initial heap size: 1 MB
  • Growth factor: 2x when expansion needed
  • Alignment: 8-byte (RAVA_ALIGN8)

gc triggering

Garbage collection is triggered automatically based on memory thresholds:

if (bytes_allocated >= next_gc) {
    gc_collect();
    next_gc = bytes_allocated * GC_GROWTH_FACTOR;
}

The threshold adjusts dynamically based on live data size, preventing both excessive collections (when memory is plentiful) and out-of-memory conditions.

manual gc control

functiondescription
gc_collect()Force immediate collection
gc_collect_if_needed()Collect only if threshold reached
gc_enable()Enable automatic collection
gc_disable()Disable automatic collection

From Java code:

System.gc();  // Suggests garbage collection

root set management

The GC tracks roots through a dynamic stack:

root stack

  • Initial capacity: 256 entries
  • Growth: Doubles when full
  • Integration: Frame stack automatically registers roots

root management functions

functiondescription
gc_push_root(obj)Register a temporary root
gc_pop_root()Remove most recent root

Temporary roots protect objects during allocation sequences:

gc_push_root(first_obj);   // Protect first
second_obj = allocate();   // Safe - first won't be collected
gc_pop_root();

re-entrancy protection

The GC prevents recursive collection using an in_collection flag:

if (gc->in_collection) return;
gc->in_collection = true;
// ... perform collection ...
gc->in_collection = false;

This ensures that allocations during finalization or tracing cannot trigger nested collections.

object lifecycle

allocation

  1. Memory allocated from heap block
  2. Object header initialized (type, flags)
  3. Object linked into allocation list
  4. GC threshold checked

finalization

Objects can register finalization callbacks that run before deallocation:

  • Finalizer runs once (OBJ_FLAG_FINALIZED prevents re-entry)
  • Object may be resurrected by finalization
  • Resurrected objects collected on next cycle

deallocation

  1. Object unlinked from allocation list
  2. Type-specific cleanup (arrays free elements, etc.)
  3. Memory returned to heap
  4. Statistics updated

string interning

Rava2 interns string constants for memory efficiency:

ObjString structure

  • chars - Character data
  • length - String length
  • hash - Precomputed hash code
  • is_interned - Whether string is in intern table

intern table

The StringInterner maintains a hash table of unique strings:

  • Duplicate string literals share the same object
  • Enables fast string comparison by pointer equality
  • Reduces memory footprint for repeated strings

memory statistics

The GC tracks allocation statistics:

metricfunction
Bytes allocatedgc_bytes_allocated()
Total bytes collectedgc_total_collected()
Collection countgc->collection_count

arena allocator

For high-performance parsing, an arena allocator is used:

characteristics

  • Bump allocation: O(1) allocation time
  • No individual frees: All memory freed at once
  • Usage: AST nodes during parsing

arena lifecycle

Arena* arena = arena_create(initial_size);
// Fast allocations during parsing
void* node = arena_alloc(arena, sizeof(ASTNode));
// ... parse complete ...
arena_destroy(arena);  // Free all at once

thread safety

Memory management is thread-safe through the global VM mutex:

  • All allocations acquire the VM lock
  • GC runs under the lock (stop-the-world)
  • Thread stacks are scanned as roots

debugging

Memory debugging tools:

make valgrind    # Run under Valgrind for leak detection
make check       # Static analysis with cppcheck

The valgrind.supp file suppresses known false positives from pthread thread-local storage.