Memory Management
Mux uses automatic reference counting (RC) for deterministic memory management without manual allocation or garbage collection.
Overview
Mux's memory model provides:
- No manual
freeordelete- Memory cleaned up automatically - Deterministic cleanup - Objects freed when reference count reaches zero
- No garbage collection pauses - Predictable performance
- Heap allocation - Objects and collections live on the heap
- Value semantics for primitives - Primitives passed by value
Memory Safety
No Null Pointers
Mux has no null pointers. Use optional<T> instead:
No Manual Memory Management
Cannot manually free memory or create dangling pointers:
No Use-After-Free
Reference counting prevents use-after-free:
Reference Counting Basics
Every heap-allocated value has a reference count that tracks how many references point to it:
Memory Layout
All heap-allocated values use a reference counting header. The RefHeader uses AtomicUsize for thread-safe atomic operations.
Automatic Cleanup
Scope-Based Cleanup
Variables are cleaned up when they go out of scope:
Early Returns
Cleanup happens even with early returns:
Reference Count Operations
Increment (mux_rc_inc)
Reference count increases when:
- Creating a new reference to existing value
- Assigning to a new variable
- Passing as a function argument
- Adding to a collection
Decrement (mux_rc_dec)
Reference count decreases when:
- Variable goes out of scope
- Variable is reassigned
- Function returns (cleanup of local variables)
When mux_rc_dec returns true, the refcount reached zero and memory is freed automatically.
Collections and Reference Counting
Collections are RC-allocated and contain RC-allocated values:
Nested Collections
When a collection is freed, all contained values have their refcounts decremented:
Objects and Reference Counting
Class instances use reference counting:
When all references are gone, the object is freed:
Scope Tracking
The compiler generates cleanup code using a scope stack:
- Enter scope ->
push_rc_scope()(function entry, if-block, loop-body, match-arm) - Track variable ->
track_rc_variable(name, alloca)for each RC-allocated variable - Exit scope ->
generate_all_scopes_cleanup()iterates through all scopes in reverse order
This ensures proper cleanup order and handles early returns.
Circular References
Warning: Mux's reference counting cannot automatically break circular references:
Solution: Avoid circular structures, or break cycles manually before scope exit.
Value vs Reference Semantics
Primitives (Value Semantics)
Primitives are passed by value (copied):
Objects (Reference Semantics)
Objects are passed by reference (shared):
Collections (Reference Semantics)
Collections are passed by reference:
References
Mux supports explicit references for passing values by reference:
Reference Syntax:
- Create reference:
&variableor&expression - Dereference:
*reference(required for both reading and writing) - Pass to functions:
func(&int ref)declares parameter,update(&x)passes reference - References to references: Not supported
Design Note: Unlike some languages with automatic dereferencing, Mux requires explicit * for all reference operations. This makes memory access patterns explicit.
Performance Considerations
Atomic Operations
Reference counting uses atomic operations for thread safety:
- Overhead: Atomic increment/decrement on each reference change
- Cache: Reference count header may cause cache misses
Compared to Garbage Collection
Advantages:
- Deterministic cleanup (no GC pauses)
- Predictable performance
- Lower memory overhead (no GC metadata)
Tradeoffs:
- Cannot break circular references automatically
- Atomic operations have cost
- Must track references carefully
Compared to Manual Management
Advantages:
- No manual
freecalls - No use-after-free bugs
- No double-free bugs
Tradeoffs:
- Cannot control exact deallocation time
- Reference count overhead
See Also
- Types - Value types and boxing
- Classes - Object lifecycle
- Collections - Collection memory management
- Error Handling - optional and result