Greetings @ML!
> I thought --d:useMalloc used in my example should do the same? Meanwhile here
> compiler prints: Error: invalid command line option: '\--useMalloc'
Yah, my bad it's `-d:useMalloc`.
> I use Google Sanitizers, while valgrind is indeed a good tool! I suggest you
> to try Dr.Memory (also nice one).
I'll check it out!
> Already did that previously, with same results. Changing to OrderedTableRef
> will not help.
My main point there was to check and make sure that there wasn't an accidental
memory leak and that avoiding ptr's helps with that by making it easier to
read/follow. The same applies with calling `createShared` or `freeShared`.
> I expected GC_fullCollect() will force collector to do it's tasks. But no..
> The more strange for me, is that I call it right before program exits, also
> sharedRam were "freed" right before it.
It's strange because you're reasoning about it incorrectly due to missing
context. ;) You can do `GC_fullCollect()` but if the GC still thinks you have a
reference to the memory it won't release it.
For a bit of context, GC's rely on the current stack or scope to know when a
piece of memory is being referenced. The natural "scopes" that Nim uses are
procs, funcs, blocks (including if/else/for loop blocks), and closures which
capture memory.
I read through your example a bit more and see some reasons why the GC won't
free the memory in table. Note the example below isn't meant to compile, but
just to give exaples. I probably typo'ed a few details. :)
Note that `freeShared` call doesn't properly tell the GC that the memory is
free, but it manually skips the GC and frees just that piece of memory. You can
use `GC_unref` to force the GC to release a reference, but that's error prone.
The more natural way to work with GC's is to think in "scopes" like proc's or
block's and let the compiler do that for you.
Here's some more detailed notes:
type
param = tuple[tbl: OrderedTableRef[string, seq[string]]]
proc theProc(attr: param) {.thread.} =
var
labels = @["Black Demon", "Turtoise", "Bill Torvalds", "BUG", "Black
Demon", "Bill Torvalds", "Linus Gates", "Linus Gates", "Linus Gates"]
for i in labels.items:
if not tbl.hasKey(i):
tbl[i] = @[]
var str: string = i
tbl[i].add str
# labels = @[] ## not needed, will be freed when `theProc` exits
## also note labels are copied to `tbl` and
still exist
proc main(): void =
block: ## adding this block with ARC/ORC will free the
## memory in `tbl` at the end of the block
## the original code kept this in scope until the end
## meaning the GC can't determine it's not used anymore
var
theTable = newOrderedTableRef[string, seq[string]]()
attr = (tbl: theTable)
thread = Thread[param]()
createThread(thread, theProc , attr)
thread.joinThreads()
var size: int = 0
debugEcho "\n Printing result from main():"
for key, sequence in theTable[].pairs:
...
## the block end's here, which will call `GC_unref` on all the variables
## in this scope. This means `theTable` should be properly freed
if not running(thread):
## freeShared(theTable) ## if you must manually free you should use
`GC_unref`
## otherwise the GC won't
know free the sub-items (at least with ARC/ORC).
## Though using a `block` or
a separate proc will
## end the the scope for
you, and call `GC_unref` for you.
GC_fullCollect() ## usually not needed with ARC, ORC use heuristics to
call
debugEcho "--------------------------------------------"
debugEcho "Size of theTable content is: ", size, ". But resources were
not freed by the collector. See Resident (RES) in linux 'top' for example."
sleep(120000)
else:
debugEcho "Thread still running, impossible scenario."
main()
Run
Hopefully that helps some!
Oh and I would second @termer and say that using ARC is the best for reasoning
about memory and freeing memory immediately when you think in terms of
"scopes". It's what I used on embedded devices where I only have kb's or RAM.