General Purpose Allocator em Zig — O que é e Como Usar

General Purpose Allocator em Zig — O que é e Como Usar

Definição

O General Purpose Allocator (GPA, std.heap.GeneralPurposeAllocator) é o alocador de uso geral da biblioteca padrão do Zig. Ele combina performance razoável com detecção integrada de bugs de memória: vazamentos (memory leaks), double-free, use-after-free e overflows de buffer. É o alocador recomendado para a maioria das aplicações e especialmente para desenvolvimento e testes.

Por que o GPA Importa

  1. Detecção de bugs: Em modo Debug, detecta automaticamente os erros de memória mais comuns.
  2. Uso geral: Funciona bem para a maioria dos padrões de alocação.
  3. Configurável: Diversas opções para ajustar comportamento e segurança.
  4. Production-ready: Pode ser usado em produção com configurações de release.

Exemplo Prático

Uso Básico com Detecção de Leaks

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const status = gpa.deinit();
        if (status == .leak) {
            std.debug.print("ALERTA: Vazamento de memória detectado!\n", .{});
        }
    }

    const allocator = gpa.allocator();

    // Alocação normal
    const dados = try allocator.alloc(u8, 100);
    defer allocator.free(dados);

    // Se esquecêssemos o defer acima, gpa.deinit() detectaria o leak
    std.debug.print("Alocado {} bytes\n", .{dados.len});
}

Configuração Personalizada

const std = @import("std");

var gpa = std.heap.GeneralPurposeAllocator(.{
    // Registra stack traces de alocação para debug
    .stack_trace_frames = 8,
    // Não libera memória de volta ao SO (mais rápido para debug)
    .retain_metadata = true,
    // Desabilita safety checks para release
    .safety = true,
}){};

Usando em Testes

const std = @import("std");
const testing = std.testing;

fn criarLista(allocator: std.mem.Allocator) !std.ArrayList(u32) {
    var lista = std.ArrayList(u32).init(allocator);
    try lista.append(1);
    try lista.append(2);
    try lista.append(3);
    return lista;
}

test "sem memory leak" {
    // testing.allocator já é um GPA com detecção de leaks!
    var lista = try criarLista(testing.allocator);
    defer lista.deinit();

    try testing.expectEqual(@as(usize, 3), lista.items.len);
    // Se esquecer o defer, o teste FALHA automaticamente
}

Detectando Double Free

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    const dados = try allocator.alloc(u8, 100);
    allocator.free(dados);
    // allocator.free(dados); // PANIC: double free detectado!
}

GPA como Alocador Principal da Aplicação

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    var app = try App.init(allocator);
    defer app.deinit();

    try app.run();
}

O que o GPA Detecta

BugDetecçãoModo
Memory leakNo deinit()Debug + ReleaseSafe
Double freeNo free()Debug + ReleaseSafe
Use-after-freeNo acessoDebug
Buffer overflowNa escritaDebug

Armadilhas Comuns

  • Esquecer de chamar deinit(): Sem deinit(), a detecção de leaks não ocorre. Sempre use defer _ = gpa.deinit().
  • Performance em hot paths: O GPA é mais lento que alocadores especializados. Para hot paths, considere ArenaAllocator.
  • Ignorar o status de deinit: O retorno de deinit() informa se houve leak. Verifique-o.
  • Usar em freestanding: O GPA depende do sistema operacional. Em embarcados, use FixedBufferAllocator.
  • Confundir com testing.allocator: testing.allocator já é um GPA configurado para testes. Não crie outro dentro de testes.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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