内存分配器 #

一、Allocator 接口 #

1.1 什么是 Allocator #

Zig 使用 Allocator 接口统一内存管理:

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

pub fn main() !void {
    // 使用页面分配器
    const allocator = std.heap.page_allocator;
    
    // 分配内存
    const buffer = try allocator.alloc(u8, 100);
    defer allocator.free(buffer);
    
    // 使用内存
    for (buffer, 0..) |*b, i| {
        b.* = @intCast(i % 256);
    }
    
    std.debug.print("Allocated {} bytes\n", .{buffer.len});
}

1.2 Allocator 方法 #

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

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    
    // 分配切片
    const slice = try allocator.alloc(i32, 10);
    defer allocator.free(slice);
    
    // 分配单个对象
    const ptr = try allocator.create(i32);
    defer allocator.destroy(ptr);
    ptr.* = 42;
    
    // 重新分配
    var buffer = try allocator.alloc(u8, 10);
    buffer = try allocator.realloc(buffer, 20);
    defer allocator.free(buffer);
    
    std.debug.print("ptr.* = {}\n", .{ptr.*});
    std.debug.print("buffer.len = {}\n", .{buffer.len});
}

二、常用分配器 #

2.1 page_allocator #

最简单的分配器,直接向操作系统申请内存:

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);
    
    std.debug.print("Allocated {} bytes\n", .{buffer.len});
}

2.2 GeneralPurposeAllocator (GPA) #

通用分配器,支持内存泄漏检测:

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const buffer = try allocator.alloc(u8, 100);
    defer allocator.free(buffer);
    
    std.debug.print("Allocated {} bytes\n", .{buffer.len});
}

2.3 ArenaAllocator #

竞技场分配器,一次性释放所有内存:

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    
    var arena = std.heap.ArenaAllocator.init(gpa.allocator());
    defer arena.deinit();
    const allocator = arena.allocator();
    
    // 多次分配
    const a = try allocator.alloc(u8, 100);
    const b = try allocator.alloc(u8, 200);
    const c = try allocator.alloc(u8, 300);
    
    _ = a;
    _ = b;
    _ = c;
    
    // 不需要单独释放,arena.deinit() 会一次性释放
    std.debug.print("Arena allocations done\n", .{});
}

2.4 FixedBufferAllocator #

固定缓冲区分配器:

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

pub fn main() !void {
    var buffer: [1024]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const allocator = fba.allocator();
    
    const data = try allocator.alloc(u8, 100);
    defer allocator.free(data);
    
    std.debug.print("Allocated {} bytes from buffer\n", .{data.len});
}

三、分配与释放 #

3.1 alloc 和 free #

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // 分配切片
    const numbers = try allocator.alloc(i32, 10);
    defer allocator.free(numbers);
    
    // 初始化
    for (numbers, 0..) |*n, i| {
        n.* = @intCast(i);
    }
    
    std.debug.print("numbers: {any}\n", .{numbers});
}

3.2 create 和 destroy #

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

const Node = struct {
    value: i32,
    next: ?*Node,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    // 创建单个对象
    const node = try allocator.create(Node);
    defer allocator.destroy(node);
    
    node.* = .{ .value = 42, .next = null };
    
    std.debug.print("node.value = {}\n", .{node.value});
}

3.3 realloc #

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    var buffer = try allocator.alloc(u8, 10);
    
    // 扩展
    buffer = try allocator.realloc(buffer, 20);
    
    // 缩小
    buffer = try allocator.realloc(buffer, 5);
    
    defer allocator.free(buffer);
    
    std.debug.print("buffer.len = {}\n", .{buffer.len});
}

四、内存泄漏检测 #

4.1 GPA 检测 #

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer {
        const leaked = gpa.deinit();
        if (leaked == .leak) {
            std.debug.print("Memory leak detected!\n", .{});
        }
    }
    const allocator = gpa.allocator();
    
    const buffer = try allocator.alloc(u8, 100);
    defer allocator.free(buffer);
    
    std.debug.print("No leaks here\n", .{});
}

五、实战示例 #

5.1 动态数组 #

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

fn DynamicArray(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,
        allocator: std.mem.Allocator,
        
        const Self = @This();
        
        fn init(allocator: std.mem.Allocator) Self {
            return .{
                .items = &.{},
                .len = 0,
                .allocator = allocator,
            };
        }
        
        fn deinit(self: *Self) void {
            if (self.items.len > 0) {
                self.allocator.free(self.items);
            }
        }
        
        fn append(self: *Self, item: T) !void {
            if (self.len >= self.items.len) {
                const new_cap = if (self.items.len == 0) 4 else self.items.len * 2;
                var new_items = try self.allocator.alloc(T, new_cap);
                @memcpy(new_items[0..self.len], self.items[0..self.len]);
                if (self.items.len > 0) {
                    self.allocator.free(self.items);
                }
                self.items = new_items;
            }
            self.items[self.len] = item;
            self.len += 1;
        }
        
        fn get(self: Self, index: usize) ?T {
            if (index >= self.len) return null;
            return self.items[index];
        }
    };
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    
    var list = DynamicArray(i32).init(gpa.allocator());
    defer list.deinit();
    
    try list.append(1);
    try list.append(2);
    try list.append(3);
    
    for (0..list.len) |i| {
        std.debug.print("list[{}] = {}\n", .{ i, list.get(i).? });
    }
}

六、总结 #

分配器要点:

分配器 用途
page_allocator 简单场景
GPA 通用场景,支持泄漏检测
ArenaAllocator 批量分配释放
FixedBufferAllocator 固定缓冲区

下一步,让我们学习高级特性!

最后更新:2026-03-27