Thread overview
Understanding GC memory ranges and roots
Jul 06, 2020
Per Nordlöw
Jul 07, 2020
Jacob Carlborg
Jul 07, 2020
rikki cattermole
Jul 08, 2020
Atila Neves
July 06, 2020
I'm experimenting with an alternative GC implementation at

https://github.com/nordlow/phobos-next/blob/master/benchmarks/gc-benchmark/source/segregated_gc.d

I now wonder if the parameters passed by `GC.addRange` and `GC.addRoot` are the only memory entry blocks I need to implement to correctly scan the stack and static memory storage?

I doesn't seem like that because when I add prints to these functions the only call I get is

addRange(0x556d8d118ea0, 93200, nil)

Is this the stack or the static storage?
I'm guessing static storage because printing the address of the first variable in a function prints 0x7ffe094fa014 which is very different from 0x556d8d118ea0.

How do I catch both the stack and static storage?

And why am I not getting any calls to GC.addRoot()? When is GC.addRoot() supposed to be called and by who?

July 07, 2020
On Monday, 6 July 2020 at 23:03:36 UTC, Per Nordlöw wrote:
> I'm experimenting with an alternative GC implementation at
>
> https://github.com/nordlow/phobos-next/blob/master/benchmarks/gc-benchmark/source/segregated_gc.d
>
> I now wonder if the parameters passed by `GC.addRange` and `GC.addRoot` are the only memory entry blocks I need to implement to correctly scan the stack and static memory storage?
>
> I doesn't seem like that because when I add prints to these functions the only call I get is
>
> addRange(0x556d8d118ea0, 93200, nil)
>
> Is this the stack or the static storage?
> I'm guessing static storage because printing the address of the first variable in a function prints 0x7ffe094fa014 which is very different from 0x556d8d118ea0.
>
> How do I catch both the stack and static storage?
>
> And why am I not getting any calls to GC.addRoot()? When is GC.addRoot() supposed to be called and by who?

The GC (at least the standard GC) works by scanning the roots. The roots are, IIRC, stack variables, global variables and TLS variables. Any memory that is reached through the roots is preserved, the rest of the memory (that has been allocated by the GC) is collected.

`GC.addRoot` and `GC.addRange` are part of the user facing API. It's intended to be called by users. For example, if you allocate some GC memory and pass that to a C library. if you don't keep any references in the D code to that memory it will be collected, even though it might be used by the C library. In this case, `GC.addRoot` can be called to add additional roots to those mentioned above.

I think you need to implemented acquiring the roots yourself. You can have a look at the current GC implementations.

--
/Jacob Carlborg
July 07, 2020
On 7/6/20 7:03 PM, Per Nordlöw wrote:
> I'm experimenting with an alternative GC implementation at
> 
> https://github.com/nordlow/phobos-next/blob/master/benchmarks/gc-benchmark/source/segregated_gc.d 
> 
> 
> I now wonder if the parameters passed by `GC.addRange` and `GC.addRoot` are the only memory entry blocks I need to implement to correctly scan the stack and static memory storage?
> 
> I doesn't seem like that because when I add prints to these functions the only call I get is
> 
> addRange(0x556d8d118ea0, 93200, nil)
> 
> Is this the stack or the static storage?
> I'm guessing static storage because printing the address of the first variable in a function prints 0x7ffe094fa014 which is very different from 0x556d8d118ea0.
> 

addRoot and addRange are called when allocations happen outside the stack or static storage. This might be the global (non-TLS) storage? It could also be something else that the runtime uses (could be on the C heap).

> How do I catch both the stack and static storage?

The stack and Thread Local Storage (i.e. static storage) are scanned using a specialized interface. See here: https://github.com/dlang/druntime/blob/eb279cf69c2e56e38b865409226f22e1d2465e72/src/core/thread/threadbase.d#L1024-L1042

> And why am I not getting any calls to GC.addRoot()? When is GC.addRoot() supposed to be called and by who?
> 

If you are adding prints, you may as well throw an exception, catch it, and print the stack trace if you want to know who's calling what.

-Steve
July 08, 2020
On 07/07/2020 11:50 PM, Steven Schveighoffer wrote:
> If you are adding prints, you may as well throw an exception, catch it, and print the stack trace if you want to know who's calling what.

You won't be able to new the Exception tho.
It'll have to be malloc'd ext.

In GC -> lock probably hit -> new -> wait on lock
July 07, 2020
On 7/7/20 6:36 PM, rikki cattermole wrote:
> On 07/07/2020 11:50 PM, Steven Schveighoffer wrote:
>> If you are adding prints, you may as well throw an exception, catch it, and print the stack trace if you want to know who's calling what.
> 
> You won't be able to new the Exception tho.
> It'll have to be malloc'd ext.

there are ways to allocate them on the stack as well, or you can simply initialize a static exception.

scope Exception ex = new Exception("nothing");
try {
   throw ex;
} catch(Exception e)
{
   // print stack trace
}

> In GC -> lock probably hit -> new -> wait on lock

Not sure, possibly printing the stack trace may be a problem as well. But it does seem like it should be doable.

-Steve
July 08, 2020
On Tuesday, 7 July 2020 at 22:36:35 UTC, rikki cattermole wrote:
> On 07/07/2020 11:50 PM, Steven Schveighoffer wrote:
>> If you are adding prints, you may as well throw an exception, catch it, and print the stack trace if you want to know who's calling what.
>
> You won't be able to new the Exception tho.
> It'll have to be malloc'd ext.
>
> In GC -> lock probably hit -> new -> wait on lock

-preview=dip1008