On 9/21/21 4:17 PM, eugene wrote:
On Tuesday, 21 September 2021 at 19:42:48 UTC, jfondren wrote:
On Monday, 13 September 2021 at 17:18:30 UTC, eugene wrote:
There's nothing special about sg0 and sg1, except that they're part of Stopper. The Stopper in main() is collected before the end of main() because it's not used later in the function

Okay, but how could you explain this then

```d
void main(string[] args) {

     auto Main = new Main();
     Main.run();

     auto stopper = new Stopper();
     stopper.run();
```

Here is what is happening. The compiler keeps track of how long it needs to keep `stopper` around.

In assembly, the `new Stopper()` call is a function which returns in a register.

On the very next instruction, you are calling the function `stopper.run` where it needs the value of the register (either pushed into an argument register, or put on the call stack, depending on the ABI). Either way, this is the last time in the function the value `stopper` is needed. Therefore, it does not store it on the stack frame of `main`. This is an optimization, but one that is taken even without optimizations enabled in some compilers. It's called [dead store elimination](https://en.wikipedia.org/wiki/Dead_store).

Since the register is overwritten by subsequent function calls, there no longer exists a reference to `stopper`, and it gets collected (along with the members that are only referenced via `stopper`).


Now, change operation order in the main like this:

```d
void main(string[] args) {

     auto Main = new Main();
     auto stopper = new Stopper();

     Main.run();
     stopper.run();
```

```
d-lang/edsm-in-d-simple-example-2 $ ./test | grep STOPPER
'STOPPER' registered 5 (esrc.Signal)
'STOPPER' registered 6 (esrc.Signal)
'STOPPER @ INIT' got 'M0' from 'SELF'
'STOPPER' enabled 5 (esrc.Signal)
'STOPPER' enabled 6 (esrc.Signal)
```

Everything is Ok now, stopper is not collected soon after start.
So the question is how this innocent looking change
can affect GC behavior so much?...

In this case, at the point you call `Main.run`, `stopper` is only in a register. Yet, it's needed later, so the compiler has no choice but to put `stopper` on the stack so it has access to it to call `stopper.run`. If it didn't, it's likely that `Main.run` will overwrite that register.

Once it's on the stack, the GC can see it for the full run of `main`. This is why this case is different.

Note that Java is even more aggressive, and might *still* collect it, because it could legitimately set `stopper` to null after the last use to signify that it's no longer needed. I don't anticipate D doing this though.

I recommend you read the blog post, it has details on how this is happening. How do you fix it? I have proposed a possible solution, but I'm not sure if it's completely sound, see [here](https://forum.dlang.org/post/sichju$2gth$1...@digitalmars.com). It may be that this works today, but a future more clever compiler can potentially see through this trick and still not store the pinned value.

I think the spec is wrong to say that just storing something as a local variable should solve the problem. We should follow the lead of other GC-supporting languages, and provide a mechanism to ensure a pointer is scannable by the GC through the entire scope.

-Steve

Reply via email to