Page Allocator em Zig — O que é e Como Usar

Page Allocator em Zig — O que é e Como Usar

Definição

O Page Allocator (std.heap.page_allocator) em Zig é o alocador mais simples disponível na biblioteca padrão. Ele solicita memória diretamente ao sistema operacional em unidades de páginas (tipicamente 4KB em x86_64). Cada chamada a alloc resulta em uma chamada de sistema (mmap no Linux, VirtualAlloc no Windows).

É o “alocador de último recurso” — confiável e simples, mas não eficiente para alocações pequenas ou frequentes.

Por que Page Allocator Importa

  1. Simplicidade: Sem estado interno, sem configuração. Está sempre disponível.
  2. Sem fragmentação: Cada alocação é independente no espaço de endereçamento.
  3. Base para outros alocadores: O ArenaAllocator e o GeneralPurposeAllocator podem usar o page allocator como backend.
  4. Ideal para alocações grandes: Quando você precisa de buffers de megabytes, é a escolha natural.

Exemplo Prático

Uso Básico

const std = @import("std");

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

    // Aloca pelo menos 1 página (4096 bytes no x86_64)
    const buffer = try allocator.alloc(u8, 1000);
    defer allocator.free(buffer);

    @memset(buffer, 'Z');
    std.debug.print("Alocado {} bytes\n", .{buffer.len});
}

Como Backend para Arena

const std = @import("std");

pub fn main() !void {
    // Arena usa page_allocator como fonte de memória
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const allocator = arena.allocator();

    // Muitas alocações pequenas — eficientes com arena
    var i: usize = 0;
    while (i < 1000) : (i += 1) {
        _ = try allocator.alloc(u8, 64);
    }
    // Tudo liberado de uma vez com arena.deinit()
}

Alocação de Buffer Grande

fn carregarArquivoGrande(caminho: []const u8) ![]u8 {
    const allocator = std.heap.page_allocator;

    const arquivo = try std.fs.cwd().openFile(caminho, .{});
    defer arquivo.close();

    const stat = try arquivo.stat();
    const buffer = try allocator.alloc(u8, stat.size);
    errdefer allocator.free(buffer);

    const lidos = try arquivo.readAll(buffer);
    return buffer[0..lidos];
}

Comparação de Tamanho de Alocação

const std = @import("std");

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

    // Mesmo pedindo 1 byte, o SO aloca uma página inteira (4096)
    const um_byte = try allocator.alloc(u8, 1);
    defer allocator.free(um_byte);

    // Pedindo exatamente uma página
    const uma_pagina = try allocator.alloc(u8, 4096);
    defer allocator.free(uma_pagina);

    std.debug.print("Pedido: 1 byte, recebido: {} bytes\n", .{um_byte.len});
    std.debug.print("Pedido: 4096 bytes, recebido: {} bytes\n", .{uma_pagina.len});
}

Quando Usar Page Allocator

SituaçãoRecomendado?
Alocações grandes (> 4KB)Sim
Muitas alocações pequenasNão (use Arena ou GPA)
Backend para outros alocadoresSim
Prototipação rápidaSim
Sistemas embarcadosNão (sem SO)

Armadilhas Comuns

  • Desperdício para alocações pequenas: Pedir 1 byte aloca uma página inteira (4096 bytes). Para dados pequenos, use GeneralPurposeAllocator ou ArenaAllocator.
  • Performance em alocações frequentes: Cada alloc/free é uma syscall. Para muitas alocações, use um alocador que faz buffering.
  • Não disponível em freestanding: Em plataformas sem SO (embarcados), não há page allocator. Use FixedBufferAllocator.
  • Alinhamento fixo: O page allocator sempre alinha a páginas. Isso é mais que suficiente para qualquer tipo de dado.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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