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
| function | description |
|---|---|
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
| color | meaning |
|---|---|
| WHITE | Unmarked - potentially garbage |
| GRAY | Marked but children not yet scanned |
| BLACK | Marked and all children scanned |
The algorithm proceeds in three phases:
- Mark roots: All root objects (stack frames, globals) are marked gray
- Trace: Gray objects are processed, marking their references and turning black
- Sweep: All white objects are freed
object types
The GC manages 11 object types:
| type | description |
|---|---|
OBJ_STRING | String instances |
OBJ_ARRAY | Array instances |
OBJ_INSTANCE | Class instances |
OBJ_CLASS | Class metadata |
OBJ_METHOD | Method definitions |
OBJ_NATIVE_METHOD | Native method bindings |
OBJ_CLOSURE | Closures with captured variables |
OBJ_UPVALUE | Captured variables |
OBJ_LAMBDA | Lambda expressions |
OBJ_STREAM | Stream objects |
OBJ_METHOD_REF | Method references |
object flags
| flag | description |
|---|---|
OBJ_FLAG_MARKED | Object is reachable |
OBJ_FLAG_FINALIZED | Finalizer 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
| function | description |
|---|---|
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
| function | description |
|---|---|
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
- Memory allocated from heap block
- Object header initialized (type, flags)
- Object linked into allocation list
- GC threshold checked
finalization
Objects can register finalization callbacks that run before deallocation:
- Finalizer runs once (
OBJ_FLAG_FINALIZEDprevents re-entry) - Object may be resurrected by finalization
- Resurrected objects collected on next cycle
deallocation
- Object unlinked from allocation list
- Type-specific cleanup (arrays free elements, etc.)
- Memory returned to heap
- Statistics updated
string interning
Rava2 interns string constants for memory efficiency:
ObjString structure
chars- Character datalength- String lengthhash- Precomputed hash codeis_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:
| metric | function |
|---|---|
| Bytes allocated | gc_bytes_allocated() |
| Total bytes collected | gc_total_collected() |
| Collection count | gc->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.