Closed PreviewCompiler source opens July 1, 2026. Playground and binary available today.Join Discord →
MetaScript

Memory Model

MetaScript uses ORC (Optimized Reference Counting) for the C backend, providing deterministic memory management without garbage collection pauses.

Why ORC?

Traditional garbage collectors have unpredictable pauses. Reference counting is predictable but slow. ORC combines the best of both:

ApproachPredictableFastNo Pauses
Tracing GC-Yes-
Naive RCYes-Yes
ORCYesYesYes

ORC achieves ~~5% overhead compared to manual memory management, while being completely automatic.

How ORC Works

Basic Reference Counting

Every object tracks how many references point to it:

const user = new User("Alice") // refcount = 1
const copy = user              // refcount = 2
// copy goes out of scope      // refcount = 1
// user goes out of scope      // refcount = 0, freed

Cycle Detection

ORC handles cycles that break naive reference counting:

class Node {
  next: Node | null = null
}

const a = new Node()
const b = new Node()
a.next = b
b.next = a // Cycle! Naive RC would leak

// ORC detects and collects cycles automatically

Move Semantics

When possible, DRC moves values instead of copying (last-read analysis). You can also use the move keyword to explicitly transfer ownership — the source is zeroed, no copy:

// Implicit move — analyzer detects last use, no copy
function processUser(user: User): void {
  console.log(user.name)
}

const user = new User("Alice")
processUser(user) // last use — moved automatically

// Explicit move — not required, for advanced performance optimization only
// Transfer ownership, source is zeroed
const y = move x;        // y owns the data, x is zeroed
return move data;         // caller takes ownership, local zeroed
consume(move buffer);     // callee takes ownership

// Useful with actors — zero-copy message passing
let state: MutableState = { count: 0 };
worker.process(move state);  // zero-copy, state invalidated
// state.count;              // COMPILE ERROR: used after move

Memory Zones

For performance-critical code, use memory zones:

import { Zone } from "@metascript/core"

Zone.scoped((zone) => {
  // All allocations use the zone
  const users = zone.alloc(User, 1000)

  for (const user of users) {
    process(user)
  }

  // Everything freed at once when zone exits
})

Next Steps