ArenaAllocator — Alocador de Arena
O ArenaAllocator é um alocador que permite liberar toda a memória alocada de uma vez. Chamadas individuais a free() são no-ops — a memória só é efetivamente liberada quando deinit() ou reset() é chamado. Isso o torna extremamente eficiente para alocações temporárias com escopo definido.
Visão Geral
const std = @import("std");
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();
O ArenaAllocator é ideal quando:
- Muitas alocações são feitas e liberadas juntas
- O tempo de vida das alocações é o mesmo (ex: processamento de uma requisição)
- Desempenho de alocação é importante
- Você quer evitar a complexidade de liberar cada alocação individualmente
Métodos
// Inicializa com um alocador subjacente
pub fn init(child_allocator: Allocator) ArenaAllocator
// Libera toda a memória
pub fn deinit(self: *ArenaAllocator) void
// Libera toda a memória mas mantém capacidade para reusar
pub fn reset(self: *ArenaAllocator, mode: ResetMode) void
// Obtém a interface Allocator
pub fn allocator(self: *ArenaAllocator) Allocator
ResetMode
pub const ResetMode = enum {
free_all, // Libera toda a memória de volta ao alocador subjacente
retain_capacity, // Mantém a memória alocada para reuso
};
Exemplo 1: Processamento de Requisição
const std = @import("std");
const Requisicao = struct {
metodo: []const u8,
caminho: []const u8,
headers: std.StringHashMap([]const u8),
};
fn processarRequisicao(arena_alloc: std.mem.Allocator) ![]const u8 {
// Todas as alocações temporárias usam o arena
var headers = std.StringHashMap([]const u8).init(arena_alloc);
try headers.put("Content-Type", "text/html");
try headers.put("Server", "Zig/0.13");
// Construir resposta
const resposta = try std.fmt.allocPrint(arena_alloc,
"HTTP/1.1 200 OK\nContent-Type: {s}\n\nOlá, mundo!",
.{headers.get("Content-Type") orelse "text/plain"},
);
return resposta;
}
pub fn main() !void {
// Arena para cada requisição
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const resp = try processarRequisicao(arena.allocator());
std.debug.print("{s}\n", .{resp});
// Tudo é liberado automaticamente pelo defer arena.deinit()
// Nenhum free individual necessário!
}
Exemplo 2: Reset para Reuso em Loop
const std = @import("std");
fn processarItem(allocator: std.mem.Allocator, id: usize) !void {
// Alocações temporárias para este item
const nome = try std.fmt.allocPrint(allocator, "item_{d}", .{id});
const dados = try allocator.alloc(u8, 1024);
@memset(dados, @intCast(id % 256));
std.debug.print("Processado: {s} ({d} bytes)\n", .{ nome, dados.len });
// Não precisa liberar — o arena cuida disso
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
// Processar 10 itens, resetando o arena entre cada um
for (0..10) |i| {
// Reset mantém a capacidade para reuso eficiente
_ = arena.reset(.retain_capacity);
try processarItem(arena.allocator(), i);
}
}
Exemplo 3: Arena como Alocador Temporário dentro de Função
const std = @import("std");
fn construirMensagem(allocator: std.mem.Allocator, partes: []const []const u8) ![]u8 {
// Arena para trabalho temporário dentro da função
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const temp = arena.allocator();
// Calcular tamanho total (alocações temporárias no arena)
var tamanho_total: usize = 0;
var separadores = try temp.alloc(usize, partes.len);
for (partes, 0..) |parte, i| {
separadores[i] = tamanho_total;
tamanho_total += parte.len;
if (i < partes.len - 1) tamanho_total += 2; // ", "
}
// Resultado final usa o alocador do chamador (não o arena!)
var resultado = try allocator.alloc(u8, tamanho_total);
var pos: usize = 0;
for (partes, 0..) |parte, i| {
@memcpy(resultado[pos..][0..parte.len], parte);
pos += parte.len;
if (i < partes.len - 1) {
resultado[pos] = ',';
resultado[pos + 1] = ' ';
pos += 2;
}
}
return resultado;
// Arena é liberado aqui, mas resultado sobrevive
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const partes = [_][]const u8{ "Zig", "é", "incrível" };
const msg = try construirMensagem(allocator, &partes);
defer allocator.free(msg);
std.debug.print("{s}\n", .{msg}); // "Zig, é, incrível"
}
Padrões Comuns
Arena por Escopo
{
var arena = std.heap.ArenaAllocator.init(base_allocator);
defer arena.deinit();
// Todas as alocações dentro deste bloco usam o arena
try fazerTrabalho(arena.allocator());
}
// Toda memória liberada aqui
Arena para Servidor Web
while (true) {
const req = try aceitar_requisicao();
_ = arena.reset(.retain_capacity);
try processar(arena.allocator(), req);
}
Resultado Longo + Temporários Curtos
fn processar(result_allocator: Allocator) ![]u8 {
var arena = std.heap.ArenaAllocator.init(result_allocator);
defer arena.deinit();
const temp = arena.allocator();
// Temporários no arena
const intermediario = try temp.alloc(u8, 1000);
_ = intermediario;
// Resultado no alocador do chamador
return try result_allocator.dupe(u8, "resultado final");
}
Módulos Relacionados
- std.mem.Allocator — Interface Allocator
- std.heap — Visão geral dos alocadores
- GeneralPurposeAllocator — Alocador de uso geral
- page_allocator — Base comum para arena
- FixedBufferAllocator — Alternativa sem syscalls