# Page Allocator

### Page Allocator

The page allocator asks the operating system for memory directly.

It works at the level of memory pages. A page is a fixed-size block of memory managed by the operating system. On many systems, one page is 4096 bytes, but you should not hard-code that assumption unless you have a reason.

The page allocator is available as:

```zig
const allocator = std.heap.page_allocator;
```

A small example:

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

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

    const buffer = try allocator.alloc(u8, 4096);
    defer allocator.free(buffer);

    buffer[0] = 42;

    std.debug.print("allocated {d} bytes\n", .{buffer.len});
}
```

This looks similar to the examples using the general purpose allocator. The difference is where the memory comes from.

With `page_allocator`, Zig asks the operating system for memory pages.

#### What Is a Page?

The operating system does not usually manage memory one byte at a time. It manages memory in blocks called pages.

A page is a unit of virtual memory.

When a program needs memory, the operating system maps pages into the process. The program can then read and write that memory.

You can think of it like this:

```text
Your program asks for memory.
The allocator asks the operating system for pages.
The operating system maps pages into your process.
The allocator gives your program a slice.
```

The page allocator is close to this operating system mechanism.

#### Why Not Use It for Everything?

The page allocator is simple, but it is not always efficient for small allocations.

Suppose you ask for 10 bytes:

```zig
const tiny = try std.heap.page_allocator.alloc(u8, 10);
defer std.heap.page_allocator.free(tiny);
```

The operating system still works in pages. Internally, this may involve much more memory and overhead than 10 bytes.

For one tiny allocation, that may not matter. For thousands of tiny allocations, it matters.

This is why the page allocator is often used as a backing allocator for another allocator.

For example, a general purpose allocator may request larger chunks from the system and then manage smaller allocations itself.

#### Page Allocator as a Backing Allocator

You have already seen this pattern:

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

const allocator = gpa.allocator();
```

The general purpose allocator manages many allocations for your program. Under the hood, it may use lower-level memory from the system.

Another common pattern is:

```zig
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();

const allocator = arena.allocator();
```

Here, the arena uses `page_allocator` as its backing allocator.

The arena gives you many fast temporary allocations. The page allocator supplies the larger memory blocks behind the arena.

#### A Simple Arena Example

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

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const allocator = arena.allocator();

    const a = try allocator.dupe(u8, "red");
    const b = try allocator.dupe(u8, "green");
    const c = try allocator.dupe(u8, "blue");

    std.debug.print("{s}, {s}, {s}\n", .{ a, b, c });
}
```

In this code, you do not free `a`, `b`, and `c` one by one.

The arena owns those allocations. When `arena.deinit()` runs, it releases its memory back through the page allocator.

#### When Page Allocator Is Useful

Use the page allocator when you want a simple allocator and do not need leak detection or complex allocation behavior.

It is useful for:

```text
simple examples
short-lived programs
backing an arena
large allocations
low-level memory experiments
```

For beginner applications, `GeneralPurposeAllocator` is often better because it can help catch leaks.

For temporary grouped allocations, `ArenaAllocator` backed by `page_allocator` is often clear and simple.

#### Large Allocations

The page allocator can be reasonable when the allocation is large.

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

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

    const megabyte = 1024 * 1024;
    const buffer = try allocator.alloc(u8, megabyte);
    defer allocator.free(buffer);

    @memset(buffer, 0);

    std.debug.print("allocated {d} bytes\n", .{buffer.len});
}
```

A large allocation fits the page allocator better than many small allocations.

But the same rule remains:

```text
If you allocate it, you need a cleanup plan.
```

#### Page Allocator Does Not Detect Leaks Like GPA

The general purpose allocator can help report leaks when deinitialized.

The page allocator is just a global allocator value. You do not create it and call `deinit` on it in the same way.

That means it is less useful when you want leak checking.

Compare:

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
```

This gives you a point where the allocator can check its internal state.

With:

```zig
const allocator = std.heap.page_allocator;
```

there is no equivalent local `gpa.deinit()` call for your program’s allocations.

So when learning, prefer this for debugging:

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

const allocator = gpa.allocator();
```

Use `page_allocator` when you specifically want its simplicity or page-level behavior.

#### Page Size and Waste

If your allocation size does not match page boundaries, the operating system still manages memory in pages.

This can waste memory for small allocations.

For example, conceptually:

```text
You ask for 24 bytes.
The system may need to reserve a page-sized region.
Most of that page is unused by your allocation.
```

A higher-level allocator can reduce this waste by placing many small allocations inside larger blocks.

That is why general purpose allocators exist.

#### Page Allocator vs General Purpose Allocator

| Allocator | Main Role | Good For | Weakness |
|---|---|---|---|
| Page allocator | Ask OS for memory pages | Large/simple allocations | Inefficient for many small allocations |
| General purpose allocator | Flexible heap management | Mixed allocation sizes | More machinery |
| Arena allocator | Grouped temporary allocation | Many values with same lifetime | Poor for independent lifetimes |
| Fixed buffer allocator | Allocate from fixed memory | Strict memory limits | Cannot grow |

The page allocator is low-level. It is useful, but it should not be your automatic answer to every allocation problem.

#### Common Beginner Pattern

For small learning programs, this is acceptable:

```zig
const allocator = std.heap.page_allocator;
```

For code where you want leak checking, use this:

```zig
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
    const status = gpa.deinit();
    if (status == .leak) {
        std.debug.print("memory leak detected\n", .{});
    }
}

const allocator = gpa.allocator();
```

For many temporary allocations with one lifetime, use this:

```zig
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();

const allocator = arena.allocator();
```

Each pattern answers a different memory question.

#### The Core Idea

The page allocator is Zig’s direct way to ask the operating system for memory pages.

It is simple and useful, especially as a backing allocator, but it is usually too low-level for many small allocations.

The rule is:

```text
Use the page allocator when page-level allocation is acceptable, or when another allocator will manage the details above it.
```

