I assumed that calling DebugAllocator.deinit frees all memory in it (independent from all the leak checking stuff). That doesn’t seem to be the case though or am I misunderstanding this somehow? Here is my snippet:
Since the inner allocator has safety = false, I expected this to run through without any output. The call to outer.deinit reports a leak though. And that’s after I called inner.deinit. Just to prove that I didn’t miss something very obvious, I added the switch to use an arena as a drop-in replacement of inner. That works so it must be in the DebugAllocator. I didn’t dig very deep yet, but the code of deinit looks good enough to me.
pub fn deinit(self: *Self) std.heap.Check {
const leaks = if (config.safety) self.detectLeaks() else false;
if (config.retain_metadata) self.freeRetainedMetadata();
self.large_allocations.deinit(self.backing_allocator);
self.* = undefined;
return if (leaks) .leak else .ok;
}
Basically, the explicit purpose of DebugAllocator is to report leaks, not handle leaks.
It doesn’t promise that it auto-frees all memory on deinit, because that’s the purpose of an arena.
So you are correct and DebugAllocator.deint() does not free the memory you allocated with it.
Yeah DebugAllocator actually doesn’t seem to free much on .deinit(), and I don’t think it’s meant to. If you look at the code, the only thing it actually does in your case is that it frees its large_allocations hash table, which seems to contain some metadata and pointers to buffers. But it doesn’t go any deeper than that. The buffers themselves are not touched.
I think generally it’s assumed that you only have a single “global” DebugAllocator (or in fact SmpAllocator, c_allocator etc.) for the runtime of your application, and then for “inner” scoped allocators you use ArenaAllocators, which do that full-sweep free on .deinit(). EDIT: This is inaccurate, see @vulpesx’s reply below: DebugAllocator.deinit does not free the memory? - #6 by vulpesx
Thanks to you all! I think the deallocation of the large_allocations threw me off. I thought that those were basically the internal pages of the allocator and if those are gone, that’s it. And I didn’t see the absence of evidence in the docs as evidence of absence of behavior
That is a reasonable assumption, but the DebugAllocator is not designed around it, it is a recognised use case to have sub DebugAllocators if you want to leak check a smaller section of code, and its backing allocator is not required to be another DebugAllocator either.
DebugAllocator is designed to verify the assumption of general purpose allocators that is you free all memory you allocate.
Honestly, it’s fair not to take the docs as evidence of anything at the present time. There’s a lot changing all the time, so the polishing of docs comes later, once everything is much more stable.
The stdlib documentation is generated from the source code, meaning it directly follows every change. Therefore, your statement isn’t entirely comprehensible.
Well, not all changes to the actual code may always be reflected in the doc comments, OR not that great care is being taken to even write said doc comments in the first place, is what I mean. Sometimes the comments do not 100% reflect reality (e.g. ArrayList.initBuffer() comment about illegal behavior needn’t be true in all cases), many parts of the library are missing docs altogether, some only contain partial notes but don’t comprehensively describe the behavior… It’s just not the current focus.
All this has me checking the source code rather than relying on the docs, for the time being. But showing source code is something Zig’s doc generator does quite well on the other hand, so I’m not complaining. And, to be clear, I’m not criticising Zig for this either, it’s all work in progress. Just advising caution when looking at the docs for now.