Notes on DebugAllocator

These musings will be obvious to some, but these are some things that took some time for me to pick up, and so I hope some others will find this useful.

The DebugAllocator is not a singleton. It is often declared at the entry point to a program and deinitialized at program exit, but you’re free to declare many DebugAllocator instances. This may be useful if you want early feedback on leaked memory before terminating the program. This is particularly useful in similar cases as the ArenaAllocator here you have some cyclical allocation pattern, and you want to be able to track leaks within the cycle.

For example, I have been writing a network service, and I have allocations that I want to tie to the lifetime of my client connections. Instead of waiting for my service to terminate to see if there were any leaks when processing the client connection, I create a new DebugAllocator for every client, and I use that for the allocations for that client, and I deinitialize that allocator when the client connection closes. This early feedback is much more convenient than killing the service every time you want to see the leak feedback.

Another interesting thing about the DebugAllocator is able to wrap arbitrary std.mem.Allocators. By default it wraps the singleton std.heap.page_allocator. Off the top of my head, some ways I can see this be particularly useful is to wrap FixedBufferAllocator or ArenaAllocator when you want to make sure that the code you are passing these allocators to would behave correctly if someone were to pass an allocator that expects individual frees.

14 Likes

This doesn’t seem to fit the Explain category, unless I missed your question? Perhaps Brainstorming is more appropriate?

It seems to fit the description of Explain to me:

Looking to understand more about Zig? You’re in the right place! The Explain category is dedicated to free-flowing discussions about the Zig language and its internals, best practices, surrounding ecosystem, and other Zig related topics.

Especially the Best practices part. I definitely learned something new.

7 Likes

Another interesting use for wrapping an ArenaAllocator is inspecting the DebugAllocators total_requested_bytes. You can establish what a normal amount of memory allocated by the arena is, and swap it out for a StackFallbackAllocator, or a FixedBufferAllocator if you establish a hard limit. The effect of this is essentially preallocating the expected memory to be used by the arena.

4 Likes

I never really thought about this, but could be quite useful for logging on leaks.

Does DebugAllocator track the maximum usage during it’s lifetime so far so that you could write something like

defer {
    std.log.debug("required {d} bytes for request", .{ debugAllocator.maximumUsage });
    if (debugAllocator.deinit() == .leak) {
        std.log.err("memory requested while handling request", .{});
    }
}

’deinit’ Will log leaks regardless.