std.heap.page_allocator em Zig — Referência e Exemplos

page_allocator — Alocador de Páginas

O std.heap.page_allocator é um alocador que solicita memória diretamente ao sistema operacional em unidades de páginas. Ele usa mmap no Linux/macOS e VirtualAlloc no Windows. É o alocador mais simples disponível e serve frequentemente como base para alocadores mais sofisticados.

Visão Geral

const std = @import("std");
const page_allocator = std.heap.page_allocator;

O page_allocator é uma constante global — não tem estado mutável e não precisa ser inicializado ou desinicializado. Cada alocação resulta em uma ou mais syscalls ao SO.

Características:

  • Alocações são sempre alinhadas ao tamanho de página (geralmente 4096 bytes)
  • Overhead mínimo de metadados
  • Cada alloc/free é uma syscall
  • Não há fragmentação interna entre alocações
  • Ideal como base para ArenaAllocator

Uso

// Acesso direto — sem inicialização necessária
const allocator = std.heap.page_allocator;

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

Exemplo 1: Uso Direto para Alocações Grandes

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    // Alocar 1 MB de memória
    const tamanho = 1024 * 1024;
    const buffer = try allocator.alloc(u8, tamanho);
    defer allocator.free(buffer);

    // Preencher com dados
    @memset(buffer, 0xAA);

    std.debug.print("Alocados {d} bytes com page_allocator\n", .{buffer.len});
    std.debug.print("Primeiros bytes: {x:0>2} {x:0>2} {x:0>2} {x:0>2}\n", .{
        buffer[0], buffer[1], buffer[2], buffer[3],
    });
}

Exemplo 2: Como Base para ArenaAllocator

O uso mais comum do page_allocator é como alocador subjacente para outros alocadores:

const std = @import("std");

pub fn main() !void {
    // Arena sobre page_allocator — combinação muito comum
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    // Muitas alocações pequenas são eficientes via arena
    var lista = std.ArrayList(u32).init(allocator);
    for (0..1000) |i| {
        try lista.append(@intCast(i));
    }

    std.debug.print("Lista com {d} itens\n", .{lista.items.len});

    // Tudo liberado de uma vez pelo arena.deinit()
}

Exemplo 3: Comparação de Granularidade

const std = @import("std");

pub fn main() !void {
    const page = std.heap.page_allocator;

    // Mesmo pedindo poucos bytes, page_allocator aloca pelo menos uma página
    const pequeno = try page.alloc(u8, 1);
    defer page.free(pequeno);

    const medio = try page.alloc(u8, 4096);
    defer page.free(medio);

    const grande = try page.alloc(u8, 10000);
    defer page.free(grande);

    std.debug.print("Pedido: 1 byte, recebido: {d} bytes\n", .{pequeno.len});
    std.debug.print("Pedido: 4096 bytes, recebido: {d} bytes\n", .{medio.len});
    std.debug.print("Pedido: 10000 bytes, recebido: {d} bytes\n", .{grande.len});

    // O page_allocator retorna exatamente o que foi pedido,
    // mas internamente aloca páginas inteiras.
    // O espaço extra dentro da última página não é acessível.
}

Padrões Comuns

Base para Arena em Servidor

// Padrão para servidor: arena sobre page_allocator
fn handler() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    try processarRequisicao(arena.allocator());
}

Alocação de Buffers Grandes

// Para buffers grandes, page_allocator é eficiente
const buffer_io = try std.heap.page_allocator.alloc(u8, 64 * 1024);
defer std.heap.page_allocator.free(buffer_io);

Quando Usar page_allocator Diretamente

Use diretamente quando:

  • Alocações são grandes e poucas
  • Você precisa de um alocador sem estado
  • É a base para outro alocador
  • Simplicidade é mais importante que eficiência para alocações pequenas

Evite usar diretamente quando:

  • Faz muitas alocações pequenas (overhead de syscalls)
  • Precisa de detecção de vazamentos (use GPA)
  • Precisa de alocações muito frequentes (use arena)

Considerações de Desempenho

Cada chamada a alloc e free resulta em uma syscall:

  • Linux: mmap/munmap
  • Windows: VirtualAlloc/VirtualFree

Syscalls têm custo na ordem de microsegundos. Para milhares de alocações pequenas, use ArenaAllocator ou GeneralPurposeAllocator como camada intermediária.

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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