Over-Allocation with the Page Allocator

The Zig standard library has a variety of useful memory allocators, each carefully designed to address a specific need by employing a specific allocation strategy. The Language Reference has an excellent section on choosing an allocator, which is highly recommended as your go-to reference for this crucial step in Zig software development.

One of these allocators is the page allocator (std.heap.page_allocator), which is often used as a backing-allocator for other higher-level allocators. The page allocator couldn’t have a more descriptive name given it does exactly what it says on the label: it allocates pages of memory. A page of memory can vary in size among processor architectures, and for example can range from 4,096 bytes to several mebibytes (MiB).

The Footgun

Each call to the page allocator’s allocation methods will allocate at least one page of memory, no matter how small the actual requested allocation size:

// Actually allocates 4 KiB on most architectures.
var two_bytes = try std.heap.page_allocator.alloc(u8, 2);

So if your intent was to just allocate 2 bytes, the page allocator is not the best option. There can be situations where you indeed want to allocate full pages of memory[1], and in those cases using the page allocator is fine; but the footgun here is not being aware of the true workings of the chosen allocator and, in this case, over-allocating memory.

One additional footgun when using the page allocator directly is not knowing that each allocation will result in a system call to the operating system, requesting at least a page of memory. System calls can be expensive in terms of performance[2], so it’s also important to be aware of this and avoid it if not required.

The Workaround

If you’re not actually interested in allocating pages of memory, avoid using std.heap.page_allocator directly. There’s absolutely no problem in using the page allocator as a backing-allocator for other allocators, of which the General Purpose Allocator should be your preferred choice if no other specific allocator is appropriate:

var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();

In Summary

Don’t use std.heap.page_allocator directly unless you want to allocate at least a full page of memory (and make a system call) with each allocation. There’s an excellent discussion about this topic here, specifically, this post by @squeek502 that provides sample code to demonstrate the behavior of the page allocator in different use cases.


  1. Maybe you’re developing your own allocator. ↩︎

  2. A system call requires a context switch between user space and kernel space. ↩︎

4 Likes

Sorry totally off topic, just thought that was a funny typo :wink:

4 Likes

LOL It eats up your memory! :smile_cat: :crocodile:

2 Likes