I am trying to force Nim to leak memory to better understand the memory model.
From my understanding, the following two programs are identical.
#include <stdlib.h>
int main() {
int *x = malloc(sizeof(int));
x = 0;
}
var mem: ptr int = cast[ptr int](alloc(sizeof(int)))
mem = nil
But passing the C program through Valgrind produces
==6255== HEAP SUMMARY:
==6255== in use at exit: 4 bytes in 1 blocks
==6255== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==6255==
==6255== LEAK SUMMARY:
==6255== definitely lost: 4 bytes in 1 blocks
==6255== indirectly lost: 0 bytes in 0 blocks
==6255== possibly lost: 0 bytes in 0 blocks
==6255== still reachable: 0 bytes in 0 blocks
==6255== suppressed: 0 bytes in 0 blocks
==6255== Rerun with --leak-check=full to see details of leaked memory
==6255==
==6255== For counts of detected and suppressed errors, rerun with: -v
==6255== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
while the Nim program produces
==6257==
==6257== HEAP SUMMARY:
==6257== in use at exit: 0 bytes in 0 blocks
==6257== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==6257==
==6257== All heap blocks were freed -- no leaks are possible
==6257==
==6257== For counts of detected and suppressed errors, rerun with: -v
==6257== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Compilation steps:
$ gcc leak.c
$ nim compile leak.nim # tried --gc:none too
Can someone help me understand what is going on? Feel free to explain using C terminology as I am very familiar with that.
Reference and pointer types says the following things:
the fact that this doesn't compile with gc:none and -d:useMalloc is a regression you could submit as an issue on github.
you can, however use gc:arc and -d:useMalloc, which then leaks as expected.
Thanks, all. I'll go through these one by one.
Just var mem = create int should do. --gc:none memory is never freed.
Using this program:
var mem = create int
mem = nil
and compiling with nim compile --gc:none leak.nim, I still do not get a memory leak.
I do not understand what specifically you do not understand
I want to understand why the above program does not produce a memory leak. Even if garbage collection is turned on, that memory should be mine to handle manually because I marked the variable as a ptr. Since I do not free it explicitly using dealloc(), I should see a memory leak in valgrind, no?
the fact that this doesn't compile with gc:none and -d:useMalloc is a regression you could submit as an issue on github.
Great, I've raise an issue: https://github.com/nim-lang/Nim/issues/15617
you can, however use gc:arc and -d:useMalloc, which then leaks as expected.
Agreed, this leaks as expected. I am stil curious, however, why it does not leak with --gc:refc -d:useMalloc. Can you explain why this is?
try to put the Nim code into a main proc
Unfortunately, putting this code in a proc main() has the same behavior as putting it outside.
I think useMalloc is necessary for valgrind to detect the leak, otherwise it is hidden by Nims TLSF allocation
Does Nim use its TLSF allocation even when the memory is a ptr? From the Nim docs, I see
Nim distinguishes between traced and untraced references. Untraced references are also called pointers. Traced references point to objects of a garbage collected heap, untraced references point to manually allocated objects or to objects somewhere else in memory.
which is why I expect a memory leak when I call alloc without a corresponding dealloc when I'm in --gc:refc mode.
I am stil curious, however, why it does not leak with --gc:refc -d:useMalloc. Can you explain why this is?
Only --gc:arc and --gc:orc supports -d:useMalloc.
Does Nim use its TLSF allocation even when the memory is a ptr?
Yes. If you're wondering, TSLF is just a memory allocator (like malloc) and does not perform any automated memory collection.
which is why I expect a memory leak when I call alloc without a corresponding dealloc when I'm in --gc:refc mode.
I actually expect this as well. At the very least it should still say that there's a reachable block of memory. I'll try to run this on my machine.
according to the docs, useMalloc >only works with gc:none and with --newruntime. which is a bit out of date, perhaps, but doublechecking the source we have, in mmdisp.nim:
elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc):
include system / mm / malloc
indeed, compiling with --gc:refc -d:useMalloc does not use calloc, and does not cause valgrind to detect a leak, while none,arc,orc do.