std.heap.ArenaAllocator 是 Zig 中极具代表性的内存管理机制。它包装一个底层的分配器(如 GPA 或 PageAllocator),允许使用者在生命周期内自由进行多次细粒度分配,而无需手动逐一释放。当 Arena 被销毁或重置时,所有分配的内存将被一次性快速回收。

典型应用场景包括:

  • 编译器或解析器中的 AST(抽象语法树)节点构建;
  • 短生命周期的网络请求处理(请求结束直接整块释放);
  • 复杂嵌套数据结构(如树、图、解析后的配置字典)的整体构建与释放。

核心用法:

  • std.heap.ArenaAllocator.init(child_allocator):创建包装底层分配器的 Arena;
  • arena.allocator():获取 std.mem.Allocator 接口供业务代码分配使用;
  • arena.deinit():整体释放 Arena 申请的所有内存;
  • arena.reset(.retain_capacity):清空已分配内容但保留已申请的内存块,用于循环处理(如批处理、网络服务循环)以避免频繁向操作系统申请内存。

//! Demonstrate memory management with std.heap.ArenaAllocator in Zig.

const std = @import("std");
const print = std.debug.print;

const Node = struct {
    value: i32,
    name: []const u8,
    children: []Node,
};

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;

    // 1. Initialize an ArenaAllocator wrapping the base allocator (GPA).
    // An arena manages a collection of memory allocations and frees them all together.
    var arena = std.heap.ArenaAllocator.init(gpa);
    // Free all allocated memory at once when leaving scope.
    defer arena.deinit();

    const arena_allocator = arena.allocator();

    // 2. Allocate multiple complex objects without worrying about freeing each one individually.
    const greeting = try std.fmt.allocPrint(arena_allocator, "Hello, {s}!", .{"Zig"});
    print("Formatted message: {s}\n", .{greeting});

    // 3. Construct a hierarchical tree structure with dynamic allocations.
    var children = try arena_allocator.alloc(Node, 2);
    children[0] = .{
        .value = 1,
        .name = try arena_allocator.dupe(u8, "child_left"),
        .children = &.{},
    };
    children[1] = .{
        .value = 2,
        .name = try arena_allocator.dupe(u8, "child_right"),
        .children = &.{},
    };

    const root = Node{
        .value = 0,
        .name = try arena_allocator.dupe(u8, "root"),
        .children = children,
    };

    print("Root: {s}, children count: {d}\n", .{ root.name, root.children.len });
    try std.testing.expectEqual(2, root.children.len);
    try std.testing.expectEqualStrings("child_left", root.children[0].name);

    // 4. Reusing an arena in batch or request-processing loops:
    // Resetting frees or retains capacity for the next iteration without recreating the arena.
    _ = arena.reset(.retain_capacity);

    const reused_slice = try arena_allocator.alloc(u32, 4);
    @memset(reused_slice, 42);
    print("Reused arena buffer values: {any}\n", .{reused_slice});
    try std.testing.expectEqual(42, reused_slice[0]);
}