Skip to content

Memory Management & Resets

zig-bind achieves high throughput by completely avoiding object-wrapper instantiation during data exchanges. Instead, it uses a pre-allocated WebAssembly linear memory pool managed by an atomic bump allocator.

Understanding how this layout functions allows you to design processing loops with absolute zero garbage collection (GC) churn.

Under the hood, zig_bind.zig establishes a static global arena with a fixed footprint:

  • Pool Size: Exactly 64 MB (64 * 1024 * 1024 bytes).
  • Alignment: Every block allocated is automatically 8-byte aligned ((bytes + 7) & ~@as(usize, 7)) to prevent CPU misaligned read traps.
  • Concurrency: The allocation offset modification uses a lock-free atomic compare-and-swap loop (@cmpxchgWeak), making allocations completely thread-safe across workers.

When you call registry.alloc() from JavaScript or TypeScript, the engine performs a zero-copy operation:

// Allocates contiguous space inside the 64MB WASM arena
const vector = registry.alloc('f32', [1.0, 2.0, 3.0, 4.0]);
  1. WASM Shift: The JavaScript agent requests memory from the exported zig_bind_alloc hook.
  2. Pointer Return: Zig bumps its internal tracking offset atomically and hands back the raw memory index pointer.
  3. Flyweight View: The JavaScript layer instantly constructs a standard TypedArray (e.g., Float32Array) directly referencing the underlying WebAssembly.Memory buffer, decorating it with a lightweight .ptr attribute.

Because memory is tracked via a singular rolling counter (pool_offset), reclaiming all allocated memory takes constant time—O(1) complexity.

// Wipe all active allocations from the current arena frame
registry.reset();

Calling registry.reset() resets pool_offset back to 0. This clears the workspace instantaneously for subsequent processing ticks without releasing memory back to the operating system or invoking the JavaScript Garbage Collector.

To prevent Out of Memory crashes, structure your applications around strict frame or transaction cycles:

function processFrame(videoFrameData: Float32Array) {
try {
// 1. Allocate space inside the frame boundary
const inputVec = registry.alloc('f32', videoFrameData);
const outputVec = registry.alloc('f32', videoFrameData.length);
// 2. Perform native calculations
const filterFn = registry.bind('apply_filter');
filterFn(inputVec, outputVec);
// 3. Extract calculations before resetting
return new Float32Array(outputVec);
} finally {
// 4. Reset memory pools for the next incoming tick
registry.reset();
}
}