Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Zig Dictionary
  3. GeneralPurposeAllocator / FixedBufferAllocator

GeneralPurposeAllocator / FixedBufferAllocator

Among Zig's allocators, the two most commonly used in everyday development are GeneralPurposeAllocator and FixedBufferAllocator. The former is a general-purpose allocator with built-in leak detection for debug builds, while the latter carves memory from a fixed buffer on the stack without touching the heap at all. Choosing the right one for each use case lets you balance both safety and performance.

GeneralPurposeAllocator syntax

// -----------------------------------------------
// Basic pattern for GeneralPurposeAllocator (GPA)
// -----------------------------------------------

// The type parameter .{} uses default settings (leak detection is enabled in debug builds)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};

// Call deinit() when leaving main to run the leak check
// It returns `.leak` so you can inspect the return value if needed
defer _ = gpa.deinit();

// Retrieve the std.mem.Allocator interface
const allocator = gpa.allocator();

// -----------------------------------------------
// Example with a common option
// -----------------------------------------------

// Disable thread safety to improve speed
var gpa_fast = std.heap.GeneralPurposeAllocator(.{ .thread_safe = false }){};
defer _ = gpa_fast.deinit();

// -----------------------------------------------
// Explicitly check for leaks via deinit()'s return value
// -----------------------------------------------

const leak_check = gpa.deinit();
if (leak_check == .leak) {
    std.log.err("Memory leak detected", .{});
}

FixedBufferAllocator syntax

// -----------------------------------------------
// Basic pattern for FixedBufferAllocator (FBA)
// -----------------------------------------------

// Prepare a buffer on the stack (adjust the size to suit your needs)
var stack_buf: [1024]u8 = undefined;

// Initialize FBA by passing the buffer
var fba = std.heap.FixedBufferAllocator.init(&stack_buf);

// Retrieve the std.mem.Allocator interface
const allocator = fba.allocator();

// -----------------------------------------------
// reset() — rewind to the start of the buffer
// -----------------------------------------------

// Calling reset() moves the internal pointer back to the start,
// allowing the same buffer to be reused.
// Do not use this if any pointers into the buffer are still live (dangling pointer risk).
fba.reset();

// -----------------------------------------------
// Check how many bytes have been used
// -----------------------------------------------

// The end_index field gives the current usage in bytes
const used = fba.end_index;

Comparison and when to use each

AllocatorHeapLeak detectionHow to freePrimary use case
GeneralPurposeAllocatorYesEnabled in debug builds.Call free() / destroy() individually.General-purpose allocator for an entire application.
FixedBufferAllocatorNoNone (exceeding the buffer returns error.OutOfMemory).Rewind the buffer with reset().Embedded environments or temporary stack-based buffers.
ArenaAllocatorYes (delegates to a parent)Depends on the parent allocator.Free everything at once with deinit().Allocating and releasing short-lived data in bulk.
ItemDescription
GeneralPurposeAllocator(.{})Creates a general-purpose allocator with default settings. Behavior can be changed via type parameters.
gpa.deinit()Releases the allocator and returns .leak if any memory was not freed. The idiom defer _ = gpa.deinit() is standard.
gpa.allocator()Returns the std.mem.Allocator interface. Pass this value to functions that need an allocator.
FixedBufferAllocator.init(&buf)Initializes FBA with an existing buffer. No heap allocation is performed.
fba.reset()Resets the internal pointer to the start of the buffer. Do not continue using old pointers after calling this.
fba.end_indexReturns the number of bytes currently in use. Useful for tracking remaining buffer capacity.
.thread_safe = falseAn option to disable GPA's thread-safety features. Use this when running single-threaded and prioritizing speed.

Sample code

kof_allocator.zig
// kof_allocator.zig — Example comparing GeneralPurposeAllocator and FixedBufferAllocator
// Uses THE KING OF FIGHTERS characters to demonstrate
// heap allocation with leak detection via GPA, and stack buffer allocation via FBA.

const std = @import("std");

// -----------------------------------------------
// Struct that holds fighter information
// -----------------------------------------------
const Fighter = struct {
    name: []const u8,  // Fighter name
    team: []const u8,  // Team name
    power: u32,        // Power rating
};

// -----------------------------------------------
// Allocates and returns a fighter's profile string using the given allocator.
// The caller is responsible for freeing the returned slice.
// -----------------------------------------------
fn buildProfile(allocator: std.mem.Allocator, f: Fighter) ![]u8 {
    return std.fmt.allocPrint(
        allocator,
        "[{s}] {s}  Power: {d}",
        .{ f.team, f.name, f.power },
    );
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // -----------------------------------------------
    // GeneralPurposeAllocator — general-purpose heap allocator
    // In debug builds, it tracks allocation/free patterns and
    // returns .leak from deinit() if any memory was not freed.
    // -----------------------------------------------

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();  // Run leak check when main exits
    const gpa_alloc = gpa.allocator();

    try stdout.print("=== KOF Fighter Profiles (GPA) ===\n\n", .{});

    // Allocate Kyo Kusanagi's profile from GPA
    const kyo_profile = try buildProfile(gpa_alloc, Fighter{
        .name  = "Kyo Kusanagi",
        .team  = "Japan Team",
        .power = 9800,
    });
    defer gpa_alloc.free(kyo_profile);  // Freed when the scope ends

    try stdout.print("{s}\n", .{kyo_profile});

    // Allocate Iori Yagami's profile from GPA
    const iori_profile = try buildProfile(gpa_alloc, Fighter{
        .name  = "Iori Yagami",
        .team  = "Yagami Team",
        .power = 9750,
    });
    defer gpa_alloc.free(iori_profile);

    try stdout.print("{s}\n", .{iori_profile});

    // Allocate Terry Bogard's profile from GPA
    const terry_profile = try buildProfile(gpa_alloc, Fighter{
        .name  = "Terry Bogard",
        .team  = "Fatal Fury Team",
        .power = 9500,
    });
    defer gpa_alloc.free(terry_profile);

    try stdout.print("{s}\n", .{terry_profile});

    // -----------------------------------------------
    // GPA + create / destroy — allocate a single struct on the heap
    // -----------------------------------------------

    try stdout.print("\n--- Allocating a single fighter with create / destroy ---\n", .{});

    // create allocates memory for one instance of Fighter and returns *Fighter
    const boss = try gpa_alloc.create(Fighter);
    defer gpa_alloc.destroy(boss);  // Frees the pointer's memory when scope ends

    boss.* = Fighter{
        .name  = "Rugal Bernstein",
        .team  = "Boss",
        .power = 99999,
    };

    try stdout.print("BOSS: [{s}] {s}  Power: {d}\n", .{ boss.team, boss.name, boss.power });

    // -----------------------------------------------
    // FixedBufferAllocator — fixed buffer on the stack
    // Carves memory from a stack buffer without touching the heap.
    // Well-suited for embedded systems and temporary stack-based processing.
    // Exceeding the buffer returns error.OutOfMemory.
    // -----------------------------------------------

    try stdout.print("\n=== KOF Fighter Profiles (FBA) ===\n\n", .{});

    // Prepare a 512-byte buffer on the stack
    var stack_buf: [512]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&stack_buf);
    const fba_alloc = fba.allocator();

    // Allocate Nakoruru's profile from FBA (stack buffer)
    const nakoruru_profile = try buildProfile(fba_alloc, Fighter{
        .name  = "Nakoruru",
        .team  = "Fatal Fury Team",
        .power = 8800,
    });
    // FBA's free is a no-op internally, but we call it for interface consistency
    defer fba_alloc.free(nakoruru_profile);

    try stdout.print("{s}\n", .{nakoruru_profile});

    // Allocate Mai Shiranui's profile from FBA
    const mai_profile = try buildProfile(fba_alloc, Fighter{
        .name  = "Mai Shiranui",
        .team  = "Fatal Fury Team",
        .power = 8900,
    });
    defer fba_alloc.free(mai_profile);

    try stdout.print("{s}\n", .{mai_profile});

    // Check how many bytes have been used
    try stdout.print("\nStack buffer usage: {d} / 512 bytes\n", .{fba.end_index});

    // -----------------------------------------------
    // FixedBufferAllocator — reuse with reset()
    // Calling reset() rewinds the pointer to the start,
    // allowing the same buffer to be reused.
    // Do not access old pointers after reset().
    // -----------------------------------------------

    try stdout.print("\n--- Reusing the buffer with FBA.reset() ---\n", .{});

    // Finish accessing existing pointers before calling reset()
    fba.reset();  // end_index returns to 0
    try stdout.print("Usage after reset: {d} bytes\n", .{fba.end_index});

    // Allocate new data after reset
    const k_profile = try buildProfile(fba_alloc, Fighter{
        .name  = "K' (K-Dash)",
        .team  = "K' Team",
        .power = 9600,
    });
    defer fba_alloc.free(k_profile);

    try stdout.print("{s}\n", .{k_profile});
    try stdout.print("Usage after reuse: {d} / 512 bytes\n", .{fba.end_index});
}
zig run kof_allocator.zig
=== KOF Fighter Profiles (GPA) ===

[Japan Team] Kyo Kusanagi  Power: 9800
[Yagami Team] Iori Yagami  Power: 9750
[Fatal Fury Team] Terry Bogard  Power: 9500

--- Allocating a single fighter with create / destroy ---
BOSS: [Boss] Rugal Bernstein  Power: 99999

=== KOF Fighter Profiles (FBA) ===

[Fatal Fury Team] Nakoruru  Power: 8800
[Fatal Fury Team] Mai Shiranui  Power: 8900

Stack buffer usage: 64 / 512 bytes

--- Reusing the buffer with FBA.reset() ---
Usage after reset: 0 bytes
[K' Team] K' (K-Dash)  Power: 9600
Usage after reuse: 32 / 512 bytes

Overview

Zig's GeneralPurposeAllocator is the most commonly used allocator for an entire application. In debug builds, it tracks allocation and deallocation patterns and returns .leak from deinit() if any memory was not freed. In release builds, the overhead is reduced and it runs faster. FixedBufferAllocator sits at the opposite end of the spectrum: it carves memory from a fixed array on the stack without touching the heap at all. Any allocation that exceeds the buffer is returned as error.OutOfMemory, making it suitable for embedded environments and performance-critical code where heap allocation must be avoided. Calling reset() rewinds the internal pointer to the start of the buffer so it can be reused, but if any old pointers are still live this creates a dangling pointer, so you must finish all accesses to old data before calling reset(). Both allocators implement the std.mem.Allocator interface, so by typing function parameters as std.mem.Allocator you can swap between them freely. See also Allocator basics for the fundamental allocator operations.

If you find any errors or copyright issues, please .