gpt4 book ai didi

memory-leaks - 使用 ArrayLists 构建切片时如何避免内存泄漏

转载 作者:行者123 更新时间:2023-12-04 08:21:24 24 4
gpt4 key购买 nike

我正在尝试使用多个 std.ArrayList 构建一片切片。

下面的代码有效,但是内存分配器 std.testing.allocator 警告我在将新元素添加到 sublist 时内存泄漏。

const std = @import("std");
const mem = std.mem;

fn sliceOfSlices(allocator: *mem.Allocator) ![][]usize {
var list = std.ArrayList([]usize).init(allocator);

var i: usize = 0;
while (i < 3) : (i += 1) {
var sublist = std.ArrayList(usize).init(allocator);
// errdefer sublist.deinit(); // here?
var n: usize = 0;
while (n < 5) : (n += 1) {
try sublist.append(n); // leaks
// errdefer sublist.deinit(); // here?
// errdefer allocator.free(sublist.items);
}
try list.append(sublist.toOwnedSlice());
}
return list.toOwnedSlice();
}

const testing = std.testing;

test "memory leaks" {
const slice = try sliceOfSlices(testing.allocator);
testing.expectEqual(@intCast(usize, 3), slice.len);
testing.expectEqual(@intCast(usize, 5), slice[0].len);
}

我尝试在几个地方使用 errdefer 来释放分配的 sublist,但是没有用。从文档来看,这似乎是一个终身问题,但我不确定如何处理它。

the std.ArrayList(T).items slice has a lifetime that remains valid until the next time the list is resized, such as by appending new elements.— https://ziglang.org/documentation/master/#Lifetime-and-Ownership

list.append() 失败时,适当的错误处理是什么?

最佳答案

我是 zig 的初学者,所以也许我在这里完全错了,但我认为你出现内存泄漏的原因不是因为某些事情失败了!

当您使用 ArrayList 时,它的内存是通过分配器分配的,内存已明确在使用结束时释放。对于 ArrayList,您可以简单地使用 deinit() 函数。但是,当您的函数 sliceOfSlices()ArrayList 包装器转换为切片时,您必须使用 testing.allocator.free(slice) 来获取摆脱该切片使用的内存。

但请注意:切片的每个元素本身就是一个切片(或指向它的指针)。也通过 ArrayList.toOwnedSlice() 获得。因此,在释放包含的切片之前,您还必须删除这些切片。

所以我会把你的测试改成

test "memory leaks" {
const slice = try sliceOfSlices(testing.allocator);
defer {
for (slice) |v| {
testing.allocator.free(v);
}
testing.allocator.free(slice);
}
testing.expectEqual(@intCast(usize, 3), slice.len);
testing.expectEqual(@intCast(usize, 5), slice[0].len);
}

现在应该不会再发生内存泄漏了。

也许有人知道更好的解决方案,但缺乏这方面的经验,这将是要走的路,IMO。

经过一番思考,回答您的问题,如果出现错误该怎么办,我会将您的函数 sliceOfSlices() 重写为

fn sliceOfSlices(allocator: *mem.Allocator) ![][]usize {
var list = std.ArrayList([]usize).init(allocator);
errdefer {
for (list.items) |slice| {
allocator.free(slice);
}
list.deinit();
}

var i: usize = 0;
while (i < 3) : (i += 1) {
var sublist = std.ArrayList(usize).init(allocator);
errdefer sublist.deinit();

var n: usize = 0;
while (n < 5) : (n += 1) {
try sublist.append(n);
}

try list.append(sublist.toOwnedSlice());
}

return list.toOwnedSlice();
}

现在,如果您的函数发生任何错误,listsublist 都应该被正确清理。尽管如此,如果函数没有返回错误,您的调用代码将负责清理以避免内存泄漏,就像在上面的 test block 中实现的那样。

关于memory-leaks - 使用 ArrayLists 构建切片时如何避免内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65480923/

24 4 0