如何解决使用 ArrayLists 构建切片时如何避免内存泄漏
我正在尝试使用多个 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
,但是没有用。从文档来看,这似乎是一个终生的问题,但我不确定如何处理。
std.ArrayList(T).items 切片的生命周期一直有效,直到下一次调整列表大小,例如添加新元素。 — 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();
}
现在,如果您的函数发生任何错误,list
和 sublist
都应该被正确清理。如果函数没有返回任何错误,您的调用代码将负责清理以避免内存泄漏,如在上面的 test
块中实现的那样。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。