---
title: "Memory Leak — Como Resolver em Zig"
url: "https://ziglang.com.br/erros/memory-leak-como-resolver-em-zig/"
markdown_url: "https://ziglang.com.br/erros/memory-leak-como-resolver-em-zig.MD"
description: "Entenda o erro de Memory Leak em Zig, que ocorre quando memória alocada nunca é liberada. Veja como detectar e corrigir vazamentos de memória."
date: "2026-02-21"
author: "Zig Brasil"
---

# Memory Leak — Como Resolver em Zig

Entenda o erro de Memory Leak em Zig, que ocorre quando memória alocada nunca é liberada. Veja como detectar e corrigir vazamentos de memória.


# Memory Leak — Como Resolver em Zig

## O Que Este Erro Significa

Um Memory Leak (vazamento de memória) ocorre quando o programa aloca memória dinamicamente mas nunca a libera. Com o tempo, a memória consumida cresce continuamente até causar `OutOfMemory` ou degradação de desempenho. Diferente de linguagens com garbage collector, em Zig toda alocação deve ser liberada manualmente.

O `GeneralPurposeAllocator` de Zig detecta vazamentos automaticamente no `deinit()`:

```
error(gpa): memory address 0x7f1234560000 has not been freed
error(gpa): leaked allocation of 1024 bytes
```

O status retornado por `gpa.deinit()` indica se houve vazamento:

```zig
const status = gpa.deinit();
if (status == .leak) {
    // Vazamento detectado!
}
```

## Causas Comuns

### 1. Esquecer de Chamar free

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

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

    const dados = try allocator.alloc(u8, 1024);
    // LEAK: nunca chama allocator.free(dados)
    _ = dados;
}
```

### 2. Retorno Antecipado sem Liberar

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

fn processar(allocator: std.mem.Allocator) !void {
    const buffer = try allocator.alloc(u8, 1024);

    const resultado = try fazerAlgo();
    if (resultado < 0) {
        return error.Invalido; // LEAK: buffer nunca é liberado!
    }

    allocator.free(buffer);
}

fn fazerAlgo() !i32 {
    return -1;
}
```

### 3. Sobrescrever Ponteiro sem Liberar o Anterior

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

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

    var dados = try allocator.alloc(u8, 100);
    // LEAK: o primeiro buffer é perdido!
    dados = try allocator.alloc(u8, 200);
    // Só o segundo será acessível para free
    allocator.free(dados);
}
```

### 4. Esquecer deinit em Coleções

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

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

    var lista = std.ArrayList(u8).init(allocator);
    try lista.append(1);
    try lista.append(2);
    try lista.append(3);
    // LEAK: nunca chama lista.deinit()
}
```

### 5. Leak em Estruturas Aninhadas

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

const Registro = struct {
    nome: []u8,
    email: []u8,
};

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

    var lista = std.ArrayList(Registro).init(allocator);
    defer lista.deinit(); // Libera a lista, mas NÃO os campos internos!

    const nome = try allocator.alloc(u8, 10);
    @memcpy(nome, "Zig Brasil");
    const email = try allocator.alloc(u8, 15);
    @memcpy(email, "zig@brasil.dev.");

    try lista.append(.{ .nome = nome, .email = email });
    // LEAK: nome e email nunca são liberados!
}
```

## Como Corrigir

### Solucao 1: Usar defer Imediatamente Após Alocar

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

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); // Garante free ao final do escopo

    // Usa dados normalmente
    dados[0] = 42;
}
```

### Solucao 2: Usar errdefer para Caminhos de Erro

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

fn processar(allocator: std.mem.Allocator) ![]u8 {
    const buffer = try allocator.alloc(u8, 1024);
    errdefer allocator.free(buffer); // Free SÓ se retornar erro

    try validar(buffer);

    return buffer; // Sucesso: transfere propriedade
}

fn validar(buf: []u8) !void {
    _ = buf;
}
```

### Solucao 3: Liberar Antes de Sobrescrever

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

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

    var dados = try allocator.alloc(u8, 100);

    // Libera o antigo antes de realocar
    allocator.free(dados);
    dados = try allocator.alloc(u8, 200);
    defer allocator.free(dados);
}
```

### Solucao 4: Limpar Coleções com Dados Aninhados

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

const Registro = struct {
    nome: []u8,
    email: []u8,
};

fn limparRegistros(allocator: std.mem.Allocator, lista: *std.ArrayList(Registro)) void {
    for (lista.items) |reg| {
        allocator.free(reg.nome);
        allocator.free(reg.email);
    }
    lista.deinit();
}

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

    var lista = std.ArrayList(Registro).init(allocator);
    defer limparRegistros(allocator, &lista);

    // Agora ao sair, tudo é liberado corretamente
}
```

### Solucao 5: ArenaAllocator para Alocações com Mesmo Ciclo de Vida

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

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

    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit(); // Libera TUDO de uma vez — impossível vazar

    const allocator = arena.allocator();

    _ = try allocator.alloc(u8, 100);
    _ = try allocator.alloc(u8, 200);
    _ = try allocator.alloc(u8, 300);
    // Sem free individual necessário — arena cuida de tudo
}
```

## Detecção de Leaks

### Usando GeneralPurposeAllocator

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{
        .verbose_log = true, // Loga cada alocação e free
    }){};
    defer {
        const status = gpa.deinit();
        if (status == .leak) {
            @panic("Vazamento de memória detectado!");
        }
    }

    const allocator = gpa.allocator();
    const buf = try allocator.alloc(u8, 100);
    _ = buf;
    // Se esquecer de liberar, o deinit reporta o leak
}
```

### Usando Valgrind

```bash
zig build-exe main.zig
valgrind --leak-check=full ./main
```

## Regras de Ouro

1. **Para cada `alloc`, tenha um `free`** correspondente.
2. **Use `defer` imediatamente** após cada alocação.
3. **Use `errdefer`** para liberar em caminhos de erro.
4. **Cuidado com dados aninhados** — libere de dentro para fora.
5. **Use ArenaAllocator** quando múltiplas alocações compartilham o ciclo de vida.
6. **Sempre teste com GeneralPurposeAllocator** em Debug para detectar leaks cedo.

Vazamentos de memória são impossíveis em linguagens com garbage collector como <a href="https://golang.com.br/artigos/go-garbage-collector/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a>, e são detectados em tempo de compilação em <a href="https://rustlang.com.br/artigos/rust-ownership-borrowing/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust graças ao sistema de ownership</a>. Em Zig, a responsabilidade é do programador, mas os allocators de debug facilitam a detecção.

## Erros Relacionados

- [OutOfMemory](/erros/erro-out-of-memory/) — Consequência de leaks acumulados
- [Allocator leak detected](/erros/erro-allocator-leak-detected/) — Detecção formal pelo alocador
- [Use after free](/erros/erro-use-after-free/) — Oposto do leak: usar após liberar
- [Double free](/erros/erro-double-free/) — Liberar a mesma memória duas vezes

## Links Úteis

- [Documentação oficial do Zig — Allocators](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
- [Receitas de gerenciamento de memória](/receitas/)
- [Tutorial sobre alocadores](/tutoriais/)
