Memory Leak em Zig — O que é e Como Evitar

Memory Leak em Zig — O que é e Como Evitar

Definição

Um memory leak (vazamento de memória) ocorre quando um programa aloca memória no heap mas nunca a libera. Com o tempo, a memória disponível diminui, podendo levar a lentidão, OutOfMemory e, eventualmente, falha total do programa.

Em Zig, onde o gerenciamento de memória é manual, memory leaks são um risco real. Porém, a linguagem oferece ferramentas excelentes para detectá-los e preveni-los: o padrão defer e o GeneralPurposeAllocator com detecção integrada.

Por que Memory Leaks Importam

  1. Degradação progressiva: O programa fica cada vez mais lento até falhar.
  2. Servidores de longa duração: Leaks pequenos se acumulam ao longo de horas/dias.
  3. Recursos limitados: Em embarcados, qualquer leak é crítico.
  4. Difícil diagnóstico: Leaks muitas vezes só se manifestam em produção.

Exemplo Prático

Memory Leak Simples

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const status = gpa.deinit();
        if (status == .leak) @panic("LEAK DETECTADO!");
    }

    const allocator = gpa.allocator();

    // LEAK! Alocamos mas nunca liberamos
    const dados = try allocator.alloc(u8, 1024);
    _ = dados;

    // Ao sair, gpa.deinit() detecta o leak
}

Corrigido com Defer

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

    const allocator = gpa.allocator();

    const dados = try allocator.alloc(u8, 1024);
    defer allocator.free(dados); // Agora está correto!

    // Usar dados...
}

Leak em Estruturas Aninhadas

const std = @import("std");

const Usuario = struct {
    nome: []u8,
    email: []u8,
    allocator: std.mem.Allocator,

    pub fn deinit(self: *Usuario) void {
        self.allocator.free(self.nome);
        self.allocator.free(self.email);
    }
};

fn criarUsuario(allocator: std.mem.Allocator) !Usuario {
    const nome = try allocator.dupe(u8, "Maria Silva");
    errdefer allocator.free(nome); // Libera nome se email falhar

    const email = try allocator.dupe(u8, "maria@email.com");
    // Sem errdefer para email — se chegarmos aqui, tudo OK

    return Usuario{
        .nome = nome,
        .email = email,
        .allocator = allocator,
    };
}

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

    var usuario = try criarUsuario(gpa.allocator());
    defer usuario.deinit(); // Libera TODOS os campos internos
}

Detecção em Testes

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

test "função não vaza memória" {
    // testing.allocator detecta leaks automaticamente
    var lista = std.ArrayList(u8).init(testing.allocator);
    defer lista.deinit(); // Sem isto, o teste FALHA

    try lista.appendSlice("Zig Brasil");
    try testing.expectEqual(@as(usize, 10), lista.items.len);
}

Padrões de Prevenção

PadrãoDescrição
defer freeLiberar imediatamente após alocar
errdefer freeLiberar em caso de erro parcial
deinit() em structsMétodos que liberam todos os campos
Arena AllocatorLiberar tudo de uma vez
GPA em testesDetecção automática em cada teste

Causas Comuns de Leaks

// 1. Sobrescrever ponteiro sem liberar
var ptr = try allocator.alloc(u8, 100);
ptr = try allocator.alloc(u8, 200); // LEAK: primeiro alloc perdido!

// 2. Return antecipado sem cleanup
const dados = try allocator.alloc(u8, 100);
if (condicao) return error.Falha; // LEAK: dados não liberados
allocator.free(dados);

// 3. Exceção em lista de inicialização
// (Usar errdefer para proteger)

Armadilhas Comuns

  • Confiar apenas em defer: Se a alocação e a liberação estão em funções diferentes, defer não resolve. Implemente deinit().
  • ArrayList/HashMap sem deinit: Coleções da std guardam memória internamente. Sempre chame deinit().
  • Leak em loops: Alocar dentro de um loop sem liberar a cada iteração acumula leaks.
  • Ignorar erros de alocação parcial: Sem errdefer, falhas no meio de inicialização vazam recursos já alocados.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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