# Memory Management

Memory management in Zig is handled *manually*, similar to C and C++. By default, variables are allocated on the stack. However, should we need some dynamic memory, we can use *allocators* to allocate memory on the heap for use.

Allocation is handled by the `std.mem.Allocator`struct, which defines methods to allocate and free memory based on some underlying allocation strategy. The Zig standard library provides several different allocators with different strategies.

## Allocators

Let's first go through a simple example using the general-purpose allocator.

### General-Purpose Allocator

The general purpose allocator in Zig can be used for most purposes. Here we use the `alloc`function of the allocator to allocate memory for 16 `u8`s (16 bytes of memory).

```zig
// We first create the general-purpose allocator.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};

// Then we get the general `std.mem.Allocator` struct from it.
// This is what we'll call to (de)allocate memory.
const allocator = gpa.allocator();

// Let's allocate 16 bytes of memory.
const some_bytes: []u8 = try allocator.alloc(u8, 16);

// Maybe put a string into it.
std.mem.copyForwards(u8, some_bytes, "Hello, my world!");

// What's in the memory?
std.debug.print("{s}\n", .{some_bytes});

// Wait, don't we need to free the memory???
```

#### defer

Here's a brief digression to introduce the `defer`keyword. This keyword can be used to execute an expression at the *end* of the current scope. If there are multiple `defer`s in the same scope, they wil be executed in the reverse order from which they were introduced.

```zig
std.debug.print("normal 1\n", .{});
defer std.debug.print("defer 1\n", .{});
std.debug.print("normal 2\n", .{});
defer std.debug.print("defer 2\n", .{});
std.debug.print("normal 3\n", .{});
defer std.debug.print("defer 3\n", .{});
```

The following statements will produce the following console output.

```
normal 1
normal 2
normal 3
defer 3
defer 2
defer 1
```

#### Checking for leaks

The example we gave earlier wasn't complete. We didn't actually *free* the memory we allocated. Should the program have been more long-running (e.g., web server), we would have leaked memory. Luckily, the general-purpose allocator comes with a built-in way to check for leaks, which composes nicely with the `defer` keyword that we just learnt.

```zig
// We first create the general-purpose allocator.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

// Then we get the general `std.mem.Allocator` struct from it.
// This is what we'll call to (de)allocate memory.
const allocator = gpa.allocator();

// Let's allocate 16 bytes of memory.
const some_bytes: []u8 = try allocator.alloc(u8, 16);

// Maybe put a string into it.
std.mem.copyForwards(u8, some_bytes, "Hello, my world!");

// What's in the memory?
std.debug.print("{s}\n", .{some_bytes});

// LEAKKKKKKKK
```

Now, the program should crash indicating where the memory leak took place. To fix this, we can use `defer` once more to free the memory at the end of the scope.

```zig
// We first create the general-purpose allocator.
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();

// Then we get the general `std.mem.Allocator` struct from it.
// This is what we'll call to (de)allocate memory.
const allocator = gpa.allocator();

// Let's allocate 16 bytes of memory.
const some_bytes: []u8 = try allocator.alloc(u8, 16);
defer allocator.free(some_bytes);

// Maybe put a string into it.
std.mem.copyForwards(u8, some_bytes, "Hello, my world!");

// What's in the memory?
std.debug.print("{s}\n", .{some_bytes});

// Phew, no more leaks!
```

### Fixed Buffer Allocator

This allocator takes in a slice of bytes and performs allocations on it. The example should make this clear.

```zig
var buf: [16]u8 = undefined;

// We first create the fixed buffer allocator.
var fba = std.heap.FixedBufferAllocator.init(&buf);

// Then we get the general `std.mem.Allocator` struct from it.
// This is what we'll call to (de)allocate memory.
const allocator = fba.allocator();

// Let's allocate 16 bytes of memory.
const some_bytes: []u8 = try allocator.alloc(u8, 16);
defer allocator.free(some_bytes);

// Maybe put a string into it.
std.mem.copyForwards(u8, some_bytes, "Hello, my world!");

// What's in the memory?
std.debug.print("{s}\n", .{some_bytes});
```

### Arena Allocator

The arena allocator wraps an existing allocator, using it the perform allocations. However, it doesn't perform any frees, instead free-ing all the memory it allocated at once upon `deinit`.

### page\_allocator

This is the most basic allocator. When you make an allocation, it will ask the OS for an entire page of memory, which makes this extremely space inefficient, and also not performant.

## ArrayList

Let's explore memory management further by looking at a common data structure used in Zig programs: the humble `ArrayList`. This is Zig's implementation of a dynamically-sized array in the standard library.

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

const allocator = gpa.allocator();

// Initialise the ArrayList, passing in the allocator it will use to dynamically
// allocate memory for its items.
var some_array_list = std.ArrayList(i32).init(allocator);
defer some_array_list.deinit(); // REMEMBER TO DEINIT WHAT YOU INIT!!

// Append some items to the list. Notice how we need to use `try` here, since the
// memory allocation can fail.
try some_array_list.append(3);
try some_array_list.append(8);
try some_array_list.append(4);
try some_array_list.append(39);

// Remove some items in the list. Notice how we don't allocate memory here, so we
// don't need to use `try`. But we need to assign the result to something.
_ = some_array_list.orderedRemove(1);

// Iterate through the array list.
for (some_array_list.items) |item| {
    std.debug.print("array list item: {}\n", .{item});
}
```

Notice how we pass `allocator` into the constructor of the `ArrayList`. It will store the allocator and use it whenever it needs to allocate memory internally. This is quite different from C, where the allocator is assumed to be a global construct (e.g., `malloc`and `free`). In this way, and since `std.mem.Allocator` represents any allocator, we can separate the concerns of *how to allocate memory* from *how to implement a dynamic list*.

However, storing the allocator means that the `ArrayList`struct takes up more space. There is another version called `ArrayListUnmanaged`that doesn't require passing the `allocator` in the constructor. Instead, you pass the `allocator` each time you need to allocate memory.

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

const allocator = gpa.allocator();

// Initialise the ArrayListUnmanaged. Notice we don't need to pass any allocator here,
// instead we just need to pass it in `deinit`.
var some_array_list = std.ArrayListUnmanaged(i32){};
defer some_array_list.deinit(allocator); // REMEMBER TO DEINIT WHAT YOU INIT!!

// Append some items to the list. Notice how we need to pass the allocator here, and also
// use `try` here, since the memory allocation can fail.
try some_array_list.append(allocator, 3);
try some_array_list.append(allocator, 8);
try some_array_list.append(allocator, 4);
try some_array_list.append(allocator, 39);

// Remove some items in the list. Notice how we don't allocate memory here, so we
// don't need to use `try` or pass any allocator. But we need to assign the result
// to something.
_ = some_array_list.orderedRemove(1);

// Iterate through the array list.
for (some_array_list.items) |item| {
    std.debug.print("array list item: {}\n", .{item});
}
```

And this highlight a common choice in Zig, whether to store the allocator or pass it in each memory-allocating operation. There isn't a correct answer, and really depends on the use-case. Hash maps in Zig also follow a similar principle, with the standard library providing both managed and unmanaged versions.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.nushackers.org/hackerschool/introduction-to-zig/memory-management.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
