Context Allocators

In Odin, memory allocation is explicitly controlled through implicit context parameters rather than hidden global allocations. Understanding how context.allocator and context.temp_allocator work allows for zero-allocation game loops and deterministic memory management.

1. The Implicit Context

Every procedure in Odin receives an implicit pointer to a Context struct unless declared with #no_context.

package main

import "core:fmt"
import "core:mem"

main :: proc() {
    // context.allocator is active here by default (usually context.default_allocator)
    buf := make([]u8, 1024)
    defer delete(buf)
}

2. Dynamic Scratchpad Allocation (context.temp_allocator)

context.temp_allocator is a ring/arena buffer ideal for per-frame temporary allocations. Instead of calling individual deallocations, reset the entire temporary memory pool at the end of each frame.

update_frame :: proc() {
    // Scratchpad memory used inside the frame
    temp_buffer := make([]Vector3, 256, context.temp_allocator)
    
    // Process game logic...
    
    // Free all temporary allocations made during this frame at once
    free_all(context.temp_allocator)
}

3. Arena Allocator Pattern

Arena allocators handle bulk memory management by reserving a continuous block of memory upfront, incrementing an offset as allocations occur.

package main

import "core:fmt"
import "core:mem"

main :: proc() {
    // Reserve a 1MB memory pool
    arena_buffer: [1 * mem.MEGABYTE]u8
    arena: mem.Arena
    mem.arena_init(&arena, arena_buffer[:])

    // Convert arena into an Allocator handle
    arena_allocator := mem.arena_allocator(&arena)

    // Divert current procedure context to use the arena
    context.allocator = arena_allocator

    // Allocations now pull directly from the arena
    data := make([]i32, 500)
    
    // Reset all arena allocations at once
    mem.arena_free_all(&arena)
}

4. Custom Tracking Allocator

To catch memory leaks during engine runtime, wrap your base allocator in a Tracking_Allocator.

tracking: mem.Tracking_Allocator
mem.tracking_allocator_init(&tracking, context.allocator)
context.allocator = mem.tracking_allocator(&tracking)

defer {
    if len(tracking.allocation_map) > 0 {
        fmt.printf("=== %v Memory Leaks Detected ===\n", len(tracking.allocation_map))
        for _, entry in tracking.allocation_map {
            fmt.printf("%v bytes leaked at %v\n", entry.size, entry.location)
        }
    }
    mem.tracking_allocator_destroy(&tracking)
}