---
title: "Memory Leak em Zig — O que é e Como Evitar"
url: "https://ziglang.com.br/glossario/memory-leak-em-zig-o-que-%C3%A9-e-como-evitar/"
markdown_url: "https://ziglang.com.br/glossario/memory-leak-em-zig-o-que-%C3%A9-e-como-evitar.MD"
description: "Entenda memory leaks (vazamentos de memória) em Zig: causas, detecção com GPA, prevenção com defer e boas práticas. Guia completo em pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# Memory Leak em Zig — O que é e Como Evitar

Entenda memory leaks (vazamentos de memória) em Zig: causas, detecção com GPA, prevenção com defer e boas práticas. Guia completo em pt-BR.


# 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

```zig
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

```zig
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

```zig
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

```zig
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ão | Descrição |
|--------|-----------|
| `defer free` | Liberar imediatamente após alocar |
| `errdefer free` | Liberar em caso de erro parcial |
| `deinit()` em structs | Métodos que liberam todos os campos |
| Arena Allocator | Liberar tudo de uma vez |
| GPA em testes | Detecção automática em cada teste |

## Causas Comuns de Leaks

```zig
// 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)
```

## Usando o Arena Allocator para Eliminar Leaks

O `ArenaAllocator` é uma estratégia poderosa: você aloca livremente, e libera tudo de uma vez ao final. É ideal para processamento de requisições, parsing ou qualquer fluxo com tempo de vida bem definido:

```zig
const std = @import("std");

pub fn processarRequisicao(backing: std.mem.Allocator) !void {
    var arena = std.heap.ArenaAllocator.init(backing);
    defer arena.deinit(); // Libera TUDO que foi alocado dentro

    const allocator = arena.allocator();

    // Pode alocar à vontade — tudo vai ser liberado junto
    const nome = try allocator.dupe(u8, "Maria");
    const sobrenome = try allocator.dupe(u8, "Silva");
    const completo = try std.fmt.allocPrint(allocator, "{s} {s}", .{ nome, sobrenome });

    std.debug.print("Nome: {s}\n", .{completo});
    // Nenhum free individual necessário!
}
```

Com o Arena, a preocupação com leaks individuais desaparece — basta garantir o `defer arena.deinit()`.

## Boas Práticas

- **`defer` logo após a alocação**: O `defer allocator.free(dados)` deve aparecer imediatamente na linha seguinte à alocação, antes de qualquer código que possa falhar.
- **`deinit()` para structs com estado interno**: Todo tipo que aloca internamente (ArrayList, HashMap, StringHashMap) deve ter um `deinit()` correspondente chamado com `defer`.
- **Teste com `testing.allocator`**: Em testes unitários, use `std.testing.allocator` — ele acusa qualquer leak ao final do teste, tornando a verificação automática.
- **Arena para escopos de curta duração**: Para processamento de uma requisição ou parsing de um documento, um Arena elimina a necessidade de rastrear cada alocação individualmente.

## 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

- [Allocator](/glossario/allocator/) — Interface de alocação de memória
- [Defer](/glossario/defer/) — Limpeza garantida ao sair do escopo
- [Errdefer](/glossario/errdefer/) — Limpeza condicional em caso de erro
- [General Purpose Allocator](/glossario/general-purpose-allocator/) — GPA com detecção de leaks
- [Dangling Pointer](/glossario/dangling-pointer/) — Ponteiros para memória liberada

## Tutoriais Relacionados

- [Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)
- [Zig Debugging](/tutoriais/zig-debugging/)
- [Testes em Zig](/tutoriais/testes-zig/)
