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)
}