Estratégias de Alocação de Memória em Zig

Estratégias de Alocação de Memória em Zig

Zig não tem garbage collector — a gestão de memória é explícita e controlada pelo desenvolvedor através de allocators. Esta flexibilidade permite escolher a estratégia ideal para cada cenário, desde alta performance até memória limitada.

Os Allocators Disponíveis

GeneralPurposeAllocator (GPA)

Para desenvolvimento e debugging — detecta leaks e double-free:

var gpa = std.heap.GeneralPurposeAllocator(.{
    .safety = true,           // Detectar use-after-free
    .stack_trace_frames = 10, // Stack traces em erros
}){};
defer {
    const status = gpa.deinit();
    if (status == .leak) std.log.err("Memory leak detectado!", .{});
}
const allocator = gpa.allocator();

ArenaAllocator

Para alocações em lote que são liberadas juntas:

var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Libera TUDO de uma vez

const allocator = arena.allocator();

// Alocar sem preocupação de liberar individualmente
const nome = try allocator.dupe(u8, "Zig Brasil");
const dados = try allocator.alloc(u8, 4096);
const lista = try allocator.alloc(u32, 1000);
// Tudo liberado no arena.deinit()

_ = nome;
_ = dados;
_ = lista;

FixedBufferAllocator

Para ambientes sem heap (embedded, kernel):

var buffer: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();

// Aloca do buffer estático — sem syscalls
const dados = try allocator.alloc(u8, 1024);
_ = dados;

Pool Allocator Personalizado

Para objetos de tamanho fixo com alocação/desalocação frequente:

fn PoolAllocator(comptime T: type) type {
    const BLOCK_SIZE = 1024;

    return struct {
        blocos: std.ArrayList([BLOCK_SIZE]T),
        livre_list: std.ArrayList(*T),
        base_allocator: std.mem.Allocator,

        const Self = @This();

        pub fn init(base: std.mem.Allocator) Self {
            return .{
                .blocos = std.ArrayList([BLOCK_SIZE]T).init(base),
                .livre_list = std.ArrayList(*T).init(base),
                .base_allocator = base,
            };
        }

        pub fn alocar(self: *Self) !*T {
            // Reutilizar objeto livre
            if (self.livre_list.popOrNull()) |ptr| return ptr;

            // Alocar novo bloco se necessário
            const bloco = try self.blocos.addOne();
            for (&bloco.*) |*item| {
                try self.livre_list.append(item);
            }
            return self.livre_list.pop();
        }

        pub fn liberar(self: *Self, ptr: *T) void {
            ptr.* = undefined; // Limpar dados sensíveis
            self.livre_list.append(ptr) catch {};
        }
    };
}

Escolhendo o Allocator Certo

CenárioAllocator Recomendado
Desenvolvimento/DebugGeneralPurposeAllocator
Request handling (HTTP)ArenaAllocator por request
Objetos fixos frequentesPool Allocator
Embedded sem heapFixedBufferAllocator
Produção genéricopage_allocator + Arena
Testesstd.testing.allocator

Conclusão

A flexibilidade de allocators em Zig permite otimizar o gerenciamento de memória para cada cenário específico. Usar ArenaAllocator para requests HTTP, FixedBufferAllocator para embedded e pools para objetos frequentes pode transformar a performance e confiabilidade da sua aplicação.

Conteúdo Relacionado

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.