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ário | Allocator Recomendado |
|---|---|
| Desenvolvimento/Debug | GeneralPurposeAllocator |
| Request handling (HTTP) | ArenaAllocator por request |
| Objetos fixos frequentes | Pool Allocator |
| Embedded sem heap | FixedBufferAllocator |
| Produção genérico | page_allocator + Arena |
| Testes | std.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
- Masterclass Memória Zig — Série completa
- Gerenciamento de Memória — Tutorial básico
- Sistemas de Tempo Real — Memória determinística
- Código Cache-Friendly — Layout de dados
- Zig em Embedded — Memória limitada