Module allocator

allocator
Stability: stable — changes additively from here.

Memory allocation abstractions and global allocator interface.

Stability

stable — Layout, AllocError, GlobalAllocator, and the pluggable surface: Allocator (a context pointer plus a vtable, Zig's std.mem.Allocator shape; it and AllocatorVTable are defined in the prelude, this module holds their methods), with_allocator, current_allocator. The vtable (alloc, realloc, free, context first) and the 16-byte owner prefix are frozen: codegen writes the same prefix for every RC object a scope places (plans/archive/EXPLICIT_ALLOCATORS.md). Implementors: the global allocator, std/arena.yo's Arena, and a counting allocator in tests/arena.test.yo.

Explicit allocators and the owner prefix

Every block handed out by Allocator.alloc carries a 16-byte prefix immediately before the returned pointer, holding the allocator's ctx and vtable. Allocator.realloc and Allocator.free read it, so a block is always released to the allocator that made it, whoever releases it and on whichever thread. Implementors never see the prefix: their alloc hands out a raw block 16 bytes larger than asked, and this module owns the layout. The same layout is emitted by codegen for reference-counted objects placed by an explicit allocator, so the two sides must never diverge.

Types

Layout struct
Layout

Describes the size and alignment requirements for a memory allocation.

Fields

NameTypeDescription
sizeusize

Size in bytes.

alignmentusize

Alignment in bytes (must be a power of two).

AllocError enum
AllocError

Memory allocation error variants.

Variants

VariantFieldsDescription
OutOfMemory

Allocator ran out of memory.

Methods
clone : (AllocError) fn(inout(self) : AllocError) -> AllocError

Parameters

NameTypeNotes
selfAllocError

Returns: AllocError

Functions

fn(comptime(T) : Type, count : usize) -> bool

Check if allocating count elements of type T would overflow usize.

Parameters

NameTypeNotes
TTypecomptime
countusize

Returns: bool

layout_of function
fn(comptime(T) : Type) -> comptime(Layout)

Get the memory layout (size and alignment) of a type at compile time.

Parameters

NameTypeNotes
TTypecomptime

Returns: comptime(Layout)

with_allocator function
fn(generic(T : Type), alloc : Allocator, f : Impl(Fn() -> T)) -> T

Evaluate f() with alloc as the thread's current allocator.

Every reference-counted object created while f runs — ref struct and enum constructors, box/arc, dyn boxes, Iso values and io.async tasks, including the ones std creates on the caller's behalf — comes from alloc, and its release is routed back to alloc wherever and whenever its last reference dies. A task created in the scope keeps it across suspensions: work it does after with_allocator has returned still lands in alloc. The previous allocator is restored when f returns or unwinds.

The scope is per thread: a spawn body starts on the global allocator (pass the Allocator value in and call with_allocator there). Container buffers created in the scope (ArrayList.new(), HashMap.new(), Deque.new(), and String/StringBuilder/HashSet through them) live in alloc too; new_in(alloc) names an allocator outside a scope. Runtime only: a compile-time call is an extern call.

arena := Arena.new(usize(1) << usize(20));
p := with_allocator(arena.allocator(), () => Point(x : i32(3), y : i32(4)));

Type Parameters

NameTypeNotes
TTypecomptime

Parameters

NameTypeNotesDescription
allocAllocator

Allocate size bytes owned by this allocator, 16-aligned. .None on failure (including a size too large to carry the prefix). Release with Allocator.free.

fImpl(Fn() -> T)

Returns: T

fn() -> Option(Allocator)

The allocator with_allocator made current on this thread, or .None when no scope is active (the global allocator). Container constructors that follow the scope read it.

Returns: Option(Allocator)

Constants

GlobalAllocator constant module (malloc : fn(size : usize) -> ?(*(void)), calloc : fn(nmemb : usize, size : usize) -> ?(*(void)), realloc : fn(ptr : ?(*(void)), size : usize) -> ?(*(void)), free : fn(ptr : ?(*(void))) -> unit, aligned_alloc : fn(alignment : usize, size : usize) -> ?(*(void)), aligned_free : fn(ptr : ?(*(void))) -> unit)

The process-wide global allocator: the one --allocator selects (system by default, mimalloc or fixed).

Two allocation families live here and they are not interchangeable:

allocate with release with
malloc / calloc / realloc free
aligned_alloc aligned_free

Crossing the two corrupts the heap on Windows, where the aligned family is the CRT's separate _aligned_malloc / _aligned_free pair.

Under --allocator fixed there is ONE family: aligned_free and free are the same function, so the split above cannot bite (the region's aligned_alloc splits a misaligned front piece back into the pool and every release path coalesces through the same TLSF free).

Value: source_namespace_18332534865118561964_0(malloc: <unknown: fn(size : usize) -> ?(*(void))>, calloc: <unknown: fn(nmemb : usize, size : usize) -> ?(*(void))>, realloc: <unknown: fn(ptr : ?(*(void)), size : usize) -> ?(*(void))>, free: <unknown: fn(ptr : ?(*(void))) -> unit>, aligned_alloc: <fn(alignment, size)>, aligned_free: <unknown: fn(ptr : ?(*(void))) -> unit>)

ALLOC_PREFIX_SIZE constant usize

Size of the owner prefix in front of every Allocator.alloc block. 16 keeps the returned pointer aligned for any scalar the plain family serves.

Value: 16