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.

Reply via email to