Introdução
O GeneralPurposeAllocator (GPA) é o alocador de uso geral recomendado em Zig para desenvolvimento e depuração. Ele fornece detecção de erros como double-free, use-after-free e vazamentos de memória, além de stack traces para ajudar a encontrar bugs. Em produção, pode ser substituído por alocadores mais performáticos como std.heap.c_allocator.
Nesta receita, você aprenderá a usar o GPA e entenderá suas funcionalidades de debug.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Uso Básico do GPA
const std = @import("std");
pub fn main() !void {
// Criar GPA com configuração padrão
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const status = gpa.deinit();
if (status == .leak) {
std.debug.print("AVISO: Vazamento de memória detectado!\n", .{});
}
}
const allocator = gpa.allocator();
// Alocar memória
const dados = try allocator.alloc(u8, 100);
defer allocator.free(dados);
// Usar a memória
@memset(dados, 0);
@memcpy(dados[0..5], "Zig!\n");
std.debug.print("{s}", .{dados[0..5]});
std.debug.print("Alocados {d} bytes\n", .{dados.len});
}
Alocar Tipos Específicos
const std = @import("std");
const Ponto = struct {
x: f64,
y: f64,
pub fn distancia(self: Ponto, outro: Ponto) f64 {
const dx = self.x - outro.x;
const dy = self.y - outro.y;
return @sqrt(dx * dx + dy * dy);
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Alocar um único struct
const p1 = try allocator.create(Ponto);
defer allocator.destroy(p1);
p1.* = .{ .x = 3.0, .y = 4.0 };
const p2 = try allocator.create(Ponto);
defer allocator.destroy(p2);
p2.* = .{ .x = 0.0, .y = 0.0 };
const dist = p1.distancia(p2.*);
std.debug.print("Ponto 1: ({d:.1}, {d:.1})\n", .{ p1.x, p1.y });
std.debug.print("Ponto 2: ({d:.1}, {d:.1})\n", .{ p2.x, p2.y });
std.debug.print("Distância: {d:.2}\n", .{dist});
// Alocar slice de structs
const pontos = try allocator.alloc(Ponto, 5);
defer allocator.free(pontos);
for (pontos, 0..) |*p, i| {
const fi: f64 = @floatFromInt(i);
p.* = .{ .x = fi * 2.0, .y = fi * 3.0 };
}
std.debug.print("\nPontos alocados:\n", .{});
for (pontos, 0..) |p, i| {
std.debug.print(" [{d}] ({d:.1}, {d:.1})\n", .{ i, p.x, p.y });
}
}
Duplicar Dados
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Duplicar string
const original = "Aprendendo Zig em português";
const copia = try allocator.dupe(u8, original);
defer allocator.free(copia);
std.debug.print("Original: {s}\n", .{original});
std.debug.print("Cópia: {s}\n", .{copia});
std.debug.print("São o mesmo ponteiro? {}\n", .{original.ptr == copia.ptr});
// Duplicar slice de inteiros
const nums = [_]i32{ 10, 20, 30, 40, 50 };
const nums_copia = try allocator.dupe(i32, &nums);
defer allocator.free(nums_copia);
std.debug.print("Cópia dos números: ", .{});
for (nums_copia) |n| std.debug.print("{d} ", .{n});
std.debug.print("\n", .{});
}
Realocar Memória
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Alocar inicialmente
var buffer = try allocator.alloc(u8, 10);
@memset(buffer, 'A');
std.debug.print("Tamanho inicial: {d}\n", .{buffer.len});
// Realocar para mais espaço
buffer = try allocator.realloc(buffer, 20);
@memset(buffer[10..], 'B');
std.debug.print("Após realloc: {d}\n", .{buffer.len});
std.debug.print("Conteúdo: {s}\n", .{buffer});
// Liberar
allocator.free(buffer);
}
Detecção de Vazamentos
O GPA detecta memória não liberada:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
const status = gpa.deinit();
if (status == .leak) {
std.debug.print("\n=== VAZAMENTO DE MEMÓRIA DETECTADO ===\n", .{});
} else {
std.debug.print("\n=== Sem vazamentos! ===\n", .{});
}
}
const allocator = gpa.allocator();
// Alocação 1: corretamente liberada
const dados1 = try allocator.alloc(u8, 100);
defer allocator.free(dados1);
// Alocação 2: corretamente liberada
const dados2 = try allocator.alloc(u32, 50);
defer allocator.free(dados2);
std.debug.print("Todas as alocações serão liberadas corretamente\n", .{});
}
GPA com Configurações Customizadas
const std = @import("std");
pub fn main() !void {
// GPA com configurações avançadas
var gpa = std.heap.GeneralPurposeAllocator(.{
.stack_trace_frames = 8, // Frames de stack trace para debug
.enable_memory_limit = true, // Habilitar limite de memória
}){};
defer _ = gpa.deinit();
// Definir limite de memória (opcional)
gpa.setRequestedMemoryLimit(1024 * 1024); // 1MB máximo
const allocator = gpa.allocator();
// Tentar alocar dentro do limite
const dados = try allocator.alloc(u8, 1000);
defer allocator.free(dados);
std.debug.print("Alocados 1000 bytes com sucesso\n", .{});
// Verificar uso de memória
std.debug.print("Memória usada aproximadamente: 1000 bytes\n", .{});
}
Padrão: Escolher Alocador em Tempo de Execução
const std = @import("std");
fn fazerTrabalho(allocator: std.mem.Allocator) !void {
const dados = try allocator.alloc(u8, 256);
defer allocator.free(dados);
@memset(dados, 0);
std.debug.print("Trabalho feito com {d} bytes\n", .{dados.len});
}
pub fn main() !void {
// Em debug: GPA para detectar erros
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
// A função aceita qualquer alocador via interface
try fazerTrabalho(gpa.allocator());
// Em produção, poderia usar:
// try fazerTrabalho(std.heap.c_allocator);
// ou
// try fazerTrabalho(std.heap.page_allocator);
}
Dicas e Boas Práticas
Use GPA durante o desenvolvimento: A detecção de erros economiza horas de depuração.
Sempre verifique
deinit(): O retorno indica se houve vazamentos.deferimediato: Coloquedefer allocator.free(x)logo após cada alocação.Aceite
std.mem.Allocatorcomo parâmetro: Funções que alocam devem receber o alocador, permitindo trocar em produção.page_allocatorpara produção: É mais simples e rápido quando não precisa de debug.
Receitas Relacionadas
- Usando ArenaAllocator - Alocação em lote
- Usando FixedBufferAllocator - Sem heap
- Detectar Vazamentos de Memória - Debug avançado
- Usando Pool Allocators - Objetos de tamanho fixo