---
title: "Gerenciamento de Memória em Zig: Allocators Explicados"
url: "https://ziglang.com.br/tutoriais/gerenciamento-de-memoria-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/gerenciamento-de-memoria-zig.MD"
description: "Tutorial completo sobre gerenciamento de memória em Zig. Aprenda sobre GeneralPurposeAllocator, FixedBufferAllocator, ArenaAllocator, PageAllocator e como criar allocators customizados. Entenda o modelo explicit over implicit do Zig."
date: "2026-02-10"
author: ""
---

# Gerenciamento de Memória em Zig: Allocators Explicados

Tutorial completo sobre gerenciamento de memória em Zig. Aprenda sobre GeneralPurposeAllocator, FixedBufferAllocator, ArenaAllocator, PageAllocator e como criar allocators customizados. Entenda o modelo explicit over implicit do Zig.


Uma das características mais distintivas de Zig é seu modelo de gerenciamento de memória. Enquanto a maioria das linguagens modernas escolhe entre **garbage collector** (Go, Java, C#) ou **borrow checker** (Rust), Zig adota uma abordagem diferente: **allocators explícitos**.

Este tutorial é um guia completo sobre como Zig gerencia memória. Se você vem de linguagens com GC, isso pode parecer assustador no início. Mas dominar allocators é essencial para escrever código Zig eficiente e seguro.

> **Pré-requisitos:** Conhecimento básico de Zig (variáveis, funções, slices). Se você é novo no Zig, comece com nosso [guia de instalação](/tutoriais/como-instalar-zig/).

## 1. Introdução: Por que Zig Não Tem Garbage Collector?

### O Problema com Garbage Collectors

Garbage collectors (GCs) automatizam o gerenciamento de memória, mas têm custos:

| Problema | Impacto |
|---|---|
| **Pause the world** | A aplicação congela durante a coleta |
| **Memória imprevisível** | Uso de memória maior que o necessário |
| **Overhead de runtime** | GC consome CPU constantemente |
| **Imprevisibilidade** | Não controle quando memória é liberada |

Para sistemas embarcados, jogos, engines de banco de dados e serviços de alta performance, esses custos são inaceitáveis.

### A Filosofia Zig: Explicit Over Implicit

Zig segue o princípio de que **comportamento implícito esconde bugs**. Em vez de um GC mágico escondido, Zig exige que você:

1. **Escolha explicitamente** qual allocator usar
2. **Passe o allocator** como parâmetro para funções que precisam de memória
3. **Libere memória explicitamente** (com `defer` ou manualmente)

Isso parece trabalhoso, mas traz benefícios enormes:

- **Controle total** sobre quando e onde memória é alocada
- **Performance previsível** — sem pauses inesperadas
- **Uso eficiente de memória** — aloque exatamente o que precisa
- **Código mais claro** — comportamento de memória é visível no código

### Comparativo de Abordagens

| Aspecto | Com GC (Go/Java) | Com Borrow Checker (Rust) | Com Allocators (Zig) |
|---|---|---|---|
| Segurança de memória | ✅ Runtime (GC) | ✅ Compilação | ⚠️ Runtime (debug builds) |
| Performance | ⚠️ Pauses | ✅ Zero-cost | ✅ Zero-cost |
| Controle | ❌ Pouco | ⚠️ Moderado | ✅ Total |
| Curva de aprendizado | ✅ Fácil | ❌ Íngreme | ⚠️ Moderada |
| Uso de memória | ❌ Overhead | ✅ Eficiente | ✅ Eficiente |

## 2. O Modelo de Allocators

Em Zig, um **allocator** é qualquer tipo que implementa a interface `Allocator`. Vamos entender como usar allocators na prática.

### A Interface Allocator

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

pub fn main() !void {
    // 1. Inicializa um allocator
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    
    // 2. Obtém a interface Allocator
    const allocator = gpa.allocator();
    
    // 3. Usa para alocar memória
    const memoria = try allocator.alloc(u8, 100);
    defer allocator.free(memoria);
    
    // 4. Usa a memória
    @memcpy(memoria, "Olá, Zig!");
    std.debug.print("{s}\n", .{memoria});
}
```

### Métodos Principais da Interface Allocator

| Método | Descrição | Retorno |
|---|---|---|
| `alloc(T, n)` | Aloca array de `n` elementos do tipo `T` | `[]T` ou `error` |
| `create(T)` | Aloca espaço para um único `T` | `*T` ou `error` |
| `resize(old, new_size)` | Redimensiona alocação existente | `bool` (sucesso) |
| `free(ptr)` | Libera memória alocada | `void` |
| `destroy(ptr)` | Libera memória de único item | `void` |

### O Padrão `try` + `defer`

O padrão idiomático em Zig é:

```zig
const memoria = try allocator.alloc(u8, tamanho);
defer allocator.free(memoria);
```

- **`try`**: Propaga erros (como OutOfMemory) para o chamador
- **`defer`**: Garante que `free` será chamado quando sair do escopo

Isso previne **memory leaks** — mesmo se a função retornar cedo por erro, a memória é liberada.

## 3. GeneralPurposeAllocator (GPA)

O **GeneralPurposeAllocator** é o "canivete suíço" dos allocators Zig. É seguro, detecta bugs, e é adequado para a maioria dos casos.

### Características do GPA

- ✅ **Segurança em debug**: Detecta double-free, use-after-free, memory leaks
- ✅ **Thread-safe**: Pode ser usado de múltiplas threads
- ⚠️ **Overhead**: Verificações adicionais em debug builds
- ✅ **Liberação individual**: Cada alocação pode ser liberada separadamente

### Uso Básico

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

pub fn main() !void {
    // Inicializa o GPA
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    
    // Importante: deinit retorna informação de leaks!
    defer {
        const deinit_status = gpa.deinit();
        if (deinit_status == .leak) {
            std.debug.print("⚠️  Memory leak detectado!\n", .{});
        }
    }
    
    const allocator = gpa.allocator();
    
    // Aloca um array
    const nomes = try allocator.alloc([]const u8, 3);
    defer allocator.free(nomes);
    
    nomes[0] = "Alice";
    nomes[1] = "Bob";
    nomes[2] = "Carol";
    
    for (nomes) |nome| {
        std.debug.print("{s}\n", .{nome});
    }
}
```

### GPA em Release Builds

Em builds de release (`-O ReleaseFast` ou `-O ReleaseSmall`), o GPA desabilita verificações de segurança, tornando-se mais rápido:

```zig
// Debug build: verificações ativadas
zig build-exe programa.zig

// Release build: otimizado, sem verificações
zig build-exe -O ReleaseFast programa.zig
```

### Quando Usar GPA

| Use GPA quando... | Não use quando... |
|---|---|
| Precisa de alocações dinâmicas variáveis | Precisa de máxima performance em hot path |
| Quer detecção de bugs em debug | Alocações em loop apertado |
| Não sabe o tamanho máximo necessário | Memória limitada (embarcados) |

## 4. FixedBufferAllocator (FBA)

O **FixedBufferAllocator** aloca memória de um buffer fixo — geralmente na stack ou em memória estática. Não faz chamadas ao sistema operacional!

### Características do FBA

- ✅ **Zero overhead**: Sem chamadas ao SO
- ✅ **Previsível**: Memória limitada a um buffer conhecido
- ✅ **Determinístico**: Sem alocações dinâmicas imprevisíveis
- ⚠️ **Limitado**: Quando o buffer acaba, alocações falham

### Uso Básico na Stack

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

pub fn processarDados(dados: []const u8) !void {
    // Buffer na stack (8KB)
    var buffer: [8192]u8 = undefined;
    
    // FBA usa esse buffer
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const allocator = fba.allocator();
    
    // Alocações vêm do buffer na stack
    const copia = try allocator.dupe(u8, dados);
    // Não precisa de defer free! Memória é liberada quando função retorna
    
    // Processa...
    std.debug.print("Processando: {s}\n", .{copia});
    
    // Buffer é automaticamente liberado quando função retorna
}

pub fn main() !void {
    try processarDados("Dados importantes");
}
```

### Uso com Memória Estática

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

// Buffer global (útil para singletons)
var buffer_global: [1024 * 1024]u8 = undefined; // 1MB
var fba = std.heap.FixedBufferAllocator.init(&buffer_global);

pub fn alocacaoEstatica() !void {
    const allocator = fba.allocator();
    
    const dados = try allocator.alloc(u8, 1000);
    // use dados...
    
    // Para reutilizar o buffer, reset:
    fba.reset();
}
```

### Resetando o FBA

```zig
// Reset libera TODA a memória de uma vez
fba.reset();

// Ou reset mantendo um ponto de rollback
const checkpoint = fba.end_index;
// ... faz alocações ...
fba.end_index = checkpoint; // "Desfaz" alocações desde checkpoint
```

### Quando Usar FBA

| Use FBA quando... | Não use quando... |
|---|---|
| Tamanho máximo é conhecido | Tamanho é imprevisível |
| Performance é crítica | Precisa de alocações que persistem além do escopo |
| Em sistemas embarcados | Precisa de thread-safety (FBA não é thread-safe) |
| Em funções que alocam temporariamente | |

## 5. Arena Allocator

O **ArenaAllocator** é um dos padrões mais úteis em Zig. Você aloca muitas vezes, mas libera **tudo de uma vez**.

### Características da Arena

- ✅ **Liberação em bulk**: Um `deinit` libera tudo
- ✅ **Rápido**: Alocação é apenas incrementar um ponteiro
- ✅ **Simples**: Não precisa rastrear alocações individuais
- ⚠️ **Sem liberação individual**: Não pode liberar itens específicos

### Uso Básico

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

pub fn processarRequisicao(allocator: std.mem.Allocator) !void {
    // Arena que usa GPA como backend
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit(); // Libera TUDO aqui
    
    const arena_allocator = arena.allocator();
    
    // Faça quantas alocações quiser...
    const headers = try arena_allocator.alloc(u8, 100);
    const body = try arena_allocator.alloc(u8, 1000);
    const response = try arena_allocator.alloc(u8, 500);
    
    // Não precisa de defer free para cada um!
    
    // Simula processamento...
    @memcpy(headers, "HTTP/1.1 200 OK");
    @memcpy(body, "Conteúdo da resposta...");
    @memcpy(response, "Resposta completa");
    
    std.debug.print("{s}\n", .{headers});
    
    // Tudo é liberado automaticamente no defer arena.deinit()
}

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

### Padrão Comum: Funções que Alocam

```zig
// Função recebe allocator como parâmetro
pub fn parseJSON(allocator: std.mem.Allocator, json: []const u8) !Value {
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();
    
    // Parse usa arena para todas as alocações temporárias
    const result = try parseInternal(arena.allocator(), json);
    
    // Duplica resultado para fora da arena (se necessário)
    return result.clone(allocator);
}
```

### Quando Usar Arena

| Use Arena quando... | Não use quando... |
|---|---|
| Muitas alocações de curta duração | Precisa liberar memória em momentos diferentes |
| Ciclo de vida é bem definido | Memória precisa persistir indefinidamente |
| Parser, compiladores, servidores HTTP | Recursos escassos que precisam ser liberados ASAP |

## 6. PageAllocator

O **PageAllocator** é o mais fundamental: pede páginas de memória diretamente ao sistema operacional.

### Características

- ✅ **Simples**: Sem overhead de bookkeeping
- ✅ **Alinhado**: Retorna memória alinhada a página
- ⚠️ **Lento**: Syscall para cada alocação
- ⚠️ **Granularidade grossa**: Aloca páginas inteiras (4KB+)

### Uso Direto

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

pub fn main() !void {
    // Page allocator direto
    const allocator = std.heap.page_allocator;
    
    // Aloca 1 byte → recebe página inteira (4KB)
    const um_byte = try allocator.alloc(u8, 1);
    defer allocator.free(um_byte);
    
    std.debug.print("Alocado: {} bytes\n", .{um_byte.len});
}
```

> **Aviso**: PageAllocator é ineficiente para pequenas alocações. Use GPA ou FBA para isso.

### Como Backend

PageAllocator é mais usado como backend para outros allocators:

```zig
// GPA usa page_allocator internamente
var gpa = std.heap.GeneralPurposeAllocator(.{
    .backing_allocator = std.heap.page_allocator,
}){};
```

## 7. CAllocator

O **CAllocator** usa `malloc`/`free` da libc — útil para interoperabilidade com código C.

### Uso

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

pub fn main() !void {
    const c_allocator = std.heap.c_allocator;
    
    const memoria = try c_allocator.alloc(u8, 100);
    defer c_allocator.free(memoria);
    
    // Pode passar para funções C que esperam malloc'd memory
}
```

### Quando Usar CAllocator

- Integrando com bibliotecas C que esperam `malloc`/`free`
- Substituindo código C gradualmente
- Quando libc já está linkada e você quer consistência

## 8. Criando um Allocator Customizado

Você pode implementar allocators customizados para casos especiais.

### Exemplo: Bump Allocator Simples

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

pub const BumpAllocator = struct {
    buffer: []u8,
    used: usize,
    
    pub fn init(buffer: []u8) BumpAllocator {
        return .{
            .buffer = buffer,
            .used = 0,
        };
    }
    
    pub fn allocator(self: *BumpAllocator) std.mem.Allocator {
        return .{
            .ptr = self,
            .vtable = &.{
                .alloc = alloc,
                .resize = resize,
                .free = free,
            },
        };
    }
    
    fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
        const self = @as(*BumpAllocator, @ptrCast(@alignCast(ctx)));
        
        // Alinha o endereço
        const addr = @intFromPtr(self.buffer.ptr) + self.used;
        const aligned_addr = std.mem.alignForward(addr, ptr_align);
        const offset = aligned_addr - @intFromPtr(self.buffer.ptr);
        
        // Verifica se cabe
        if (offset + len > self.buffer.len) return null;
        
        self.used = offset + len;
        return self.buffer.ptr + offset;
    }
    
    fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool {
        _ = ctx; _ = buf; _ = buf_align; _ = new_len; _ = ret_addr;
        // Bump allocator não suporta resize
        return false;
    }
    
    fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
        _ = ctx; _ = buf; _ = buf_align; _ = ret_addr;
        // Bump allocator não libera individualmente
    }
    
    pub fn reset(self: *BumpAllocator) void {
        self.used = 0;
    }
};

pub fn main() !void {
    var buffer: [1024]u8 = undefined;
    var bump = BumpAllocator.init(&buffer);
    
    const allocator = bump.allocator();
    const dados = try allocator.alloc(u8, 100);
    
    std.debug.print("Alocado: {} bytes em {:p}\n", .{dados.len, dados.ptr});
}
```

## 9. Error Handling com Allocators

Alocações podem falhar (OutOfMemory). Em Zig, isso é tratado explicitamente.

### O Erro OutOfMemory

```zig
const memoria = allocator.alloc(u8, tamanho_enorme);

// Tratamento com if/else
if (memoria) |mem| {
    // Sucesso, use mem
    defer allocator.free(mem);
} else |err| {
    std.debug.print("Falha ao alocar: {}\n", .{err});
    return err;
}
```

### Padrão try/catch

```zig
pub fn alocacaoSegura(allocator: std.mem.Allocator) !void {
    // Propaga erro para o chamador
    const memoria = try allocator.alloc(u8, 1000);
    defer allocator.free(memoria);
    
    // Ou trata localmente
    const mais_memoria = allocator.alloc(u8, 1_000_000) catch |err| {
        std.log.err("Não foi possível alocar 1MB: {}", .{err});
        return error.AlocacaoGrandeFalhou;
    };
    defer allocator.free(mais_memoria);
}
```

### Graceful Degradation

```zig
pub fn processarComFallback(allocator: std.mem.Allocator, dados: []const u8) !void {
    // Tenta alocar otimizado
    const buffer = allocator.alloc(u8, dados.len * 2) catch {
        // Fallback: processa em chunks menores
        return processarEmChunks(allocator, dados);
    };
    defer allocator.free(buffer);
    
    // Processamento otimizado...
}
```

## 10. Melhores Práticas e Padrões Comuns

### 1. Sempre Use `defer` para Liberar

```zig
// ✅ Bom
const mem = try allocator.alloc(u8, 100);
defer allocator.free(mem);

// ❌ Ruim - esqueceu de liberar
const mem = try allocator.alloc(u8, 100);
// ... código ...
// ops, memory leak!
```

### 2. Passe Allocator como Parâmetro

```zig
// ✅ Bom - caller escolhe o allocator
pub fn parse(allocator: std.mem.Allocator, input: []const u8) !Result {
    const temp = try allocator.alloc(u8, input.len);
    defer allocator.free(temp);
    // ...
}

// ❌ Ruim - esconde dependência de allocator
pub fn parse(input: []const u8) !Result {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    // caller não sabe que você está alocando!
}
```

### 3. Use Arena para Alocações Temporárias

```zig
// ✅ Arena simplifica gerenciamento
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();

const a = try arena.allocator().alloc(u8, 100);
const b = try arena.allocator().alloc(u8, 200);
const c = try arena.allocator().alloc(u8, 300);
// Um único defer libera tudo!
```

### 4. Escolha o Allocator Certo para o Caso

| Cenário | Allocator Recomendado |
|---|---|
| Uso geral | GPA |
| Alocações temporárias de tamanho conhecido | FBA |
| Parser, compilador | Arena |
| Sistemas embarcados | FBA ou customizado |
| Hot path, máxima performance | FBA ou Pool |
| Interop com C | CAllocator |

### 5. Detecte Memory Leaks em Debug

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
    const status = gpa.deinit();
    if (status == .leak) {
        @panic("Memory leak detectado!");
    }
}
```

## Conclusão

O modelo de allocators do Zig pode parecer trabalhoso inicialmente, mas oferece:

- **Controle total** sobre gerenciamento de memória
- **Performance previsível** sem pauses de GC
- **Flexibilidade** para escolher a estratégia certa para cada caso
- **Detecção de bugs** em debug builds

Dominar allocators é essencial para escrever código Zig idiomático e eficiente. Comece com GPA para casos gerais, experimente FBA para performance crítica, e use Arena quando fizer sentido. Para uma perspectiva comparativa, veja como <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 resolve o gerenciamento de memória com ownership e borrowing</a> ou 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 delega a tarefa ao garbage collector</a>.

### Próximos Passos

- 📚 Leia sobre [comptime em Zig](/tutoriais/comptime-em-zig/) para metaprogramação
- 🔧 Explore nosso [guia de build system](/tutoriais/zig-build-system/)
- 🌐 Experimente compilar para [WebAssembly](/tutoriais/zig-webassembly-wasm/)

### Recursos Adicionais

- [Zig Guide - Allocators](https://zig.guide/standard-library/allocators/)
- [Introduction to Zig - Memory](https://pedropark99.github.io/zig-book/Chapters/01-memory.html)
- [Zighelp - Standard Patterns](https://zighelp.org/chapter-2/)

---

*Dúvidas sobre allocators? Junte-se à nossa comunidade no Discord!*
