微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决使用 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();
}

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。