Zig 语言不干预堆内存分配, 没有隐藏的内存分配,没有new关键字或其他任何使用堆分配器的语言功能,整个堆都是由库或者用户代码而非语言本身所管理的。
Zig 标准库提供了一中分配内存的模式,这允许程序员精准选择标准库中内存如何完成分配,在标准库中不会背着你偷偷分配内存。
Zig 提供的分配器 allocator
有:
defer
释放内存惯用模式是使用defer
。defer
在退出作用域时指定给定的代码,『作用域退出』包括到达作用域的结尾或从作用域返回。
Zig 的 defer 类似于 Go 的 defer,但存在一个主要区别。在 Zig 中,defer 将在其包含作用域的末尾运行。在 Go 中,defer 是在包含函数的末尾运行。
通过使用defer
,指定分配器在退出作用域时释放分配的内存。
1
2
3
4
5
6
7
8
9
10
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
var arr = try allocator.alloc(usize, try getRandomCount());
defer allocator.free(arr);
for (0..arr.len) |i| {
arr[i] = i;
}
std.debug.print("{any}\n", .{arr});
|
std.heap.page_allocator
这是最基本的分配器,线程安全且无锁,每次内存的分配(allocation)和释放(free),都会触发一次直接的系统调用,它会直接要求操作系统分配或者释放整页内存,
即使是单直接内存分配也可能导致保留数千字节的内存。由于通过系统调用要求操作系统 OS 分配内存,这对于速读来说也是及其低效的。
1
2
3
4
5
6
7
8
9
10
11
12
|
const std = @import("std");
const expect = std.testing.expect;
test "allocation" {
const allocator = std.heap.page_allocator;
const memory = try allocator.alloc(u8, 100);
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
const page_allocator: type = if (builtin.target.isWasm())
Allocator{
.ptr = undefined,
.vtable = &WasmPageAllocator.vtable,
}
else if (builtin.target.os.tag == .plan9)
Allocator{
.ptr = undefined,
.vtable = &SbrkAllocator(std.os.plan9.sbrk).vtable,
}
else if (builtin.target.os.tag == .freestanding)
root.os.heap.page_allocator
else
Allocator{
.ptr = undefined,
.vtable = &PageAllocator.vtable,
};
|
std.heap.GeneralPurposeAllocator
这是一种通用的、线程安全的分配器,可以作为程序的主分配器。这是一个安全的分配器,可以防止双重释放( double-free
)、使用后释放( use-after-free
)
还可以检测泄漏。可以通过它的配置结构(.{}
),将安全检测和线程安全关闭掉:
1
2
3
4
5
6
7
|
test "GPA" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const bytes = try allocator.alloc(u8, 100);
defer allocator.free(bytes);
}
|
std.heap.FixedBufferAllocator
std.heap.ArenaAllocator
std.testing.allocator