Arena Allocator em Zig — O que é e Como Usar
Definição
O Arena Allocator (std.heap.ArenaAllocator) em Zig é um alocador que agrupa múltiplas alocações e as libera todas de uma vez com uma única operação (deinit ou reset). Chamadas individuais a free são no-ops (não fazem nada). A arena obtém memória de um alocador “pai” (backing allocator) em blocos grandes e distribui fatias desses blocos.
É o alocador mais eficiente quando você tem um padrão de “alocar muita coisa, depois liberar tudo junto”.
Por que Arena Allocator Importa
- Performance: Alocação é quase instantânea (incrementa um ponteiro). Liberação é O(1).
- Simplicidade: Não precisa gerenciar
freeindividual —deinitlibera tudo. - Sem fragmentação: Alocações são sequenciais dentro dos blocos.
- Padrão ideal para requisições: Aloca durante o processamento, libera ao final da requisição.
Exemplo Prático
Uso Básico
const std = @import("std");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Libera TODA a memória de uma vez
const allocator = arena.allocator();
// Todas essas alocações são rápidas
const nome = try allocator.alloc(u8, 100);
const dados = try allocator.alloc(u32, 500);
const config = try allocator.create(Config);
// Não precisa de free individual!
// arena.deinit() cuida de tudo
_ = nome;
_ = dados;
_ = config;
}
const Config = struct { porta: u16, debug: bool };
Padrão de Requisição HTTP
fn handleRequest(request: *Request, response: *Response) !void {
// Arena para esta requisição
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit(); // Tudo limpo ao final da requisição
const alloc = arena.allocator();
const body = try parseBody(alloc, request.body);
const usuario = try buscarUsuario(alloc, body.user_id);
const html = try renderTemplate(alloc, "perfil.html", usuario);
try response.send(html);
// Sem memory leak possível — arena libera tudo
}
Reset para Reutilização
fn processarLote(items: []const Item) !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
for (items) |item| {
// Reset mantém a memória alocada do SO, mas permite reutilizar
_ = arena.reset(.retain_capacity);
const alloc = arena.allocator();
const resultado = try processarItem(alloc, item);
try salvarResultado(resultado);
}
}
Arena como Scratch Allocator
fn algoritmoComplexo(allocator: std.mem.Allocator) !Resultado {
// Arena para alocações temporárias internas
var scratch = std.heap.ArenaAllocator.init(allocator);
defer scratch.deinit();
const temp = scratch.allocator();
// Muitas alocações temporárias durante o cálculo
var intermediario = try temp.alloc(f64, 10000);
_ = intermediario;
var buffer = try temp.alloc(u8, 4096);
_ = buffer;
// Resultado final alocado com o allocator "real"
const resultado = try allocator.create(Resultado);
resultado.* = calcular();
return resultado;
}
Quando Usar Arena
| Cenário | Arena é boa? |
|---|---|
| Processamento de requisição | Sim |
| Compilador/parser (por fase) | Sim |
| Game loop (por frame) | Sim |
| Estrutura de longa duração | Não (use GPA) |
| Alocações com lifetimes variados | Não |
Armadilhas Comuns
- Reter referências após deinit/reset: Qualquer ponteiro obtido da arena se torna inválido após
deinitoureset. Isso cria dangling pointers. - Arena que nunca é liberada: Em loops longos sem
reset, a arena acumula memória indefinidamente. - Free individual não faz nada: Chamar
freeem uma alocação de arena não libera memória. Isso é por design. - Escolher o backing allocator errado: Use
page_allocatorpara programas normais, ou outro allocator para contextos especiais.
Termos Relacionados
- Allocator — Interface de alocação de memória
- Page Allocator — Alocador baseado em páginas
- General Purpose Allocator — Alocador de uso geral
- Memory Leak — Vazamentos de memória
- Stack vs Heap — Diferença entre pilha e heap