OK, so here is what Actually happens consistently (all this is with dmd):
- Macos 64-bit: Option 2
- Windows 64-bit: Option 1
- Windows 32-bit: Option 2
- Windows 32-bit with -g: Option 1
- Linux 64-bit: Option 1
- Linux 32-bit: Option 2
I'm sure other compilers would have varying degrees of options.
What is happening?
What happens is that the variable c
is sometimes not stored on the stack, but in a register. after a call to GC.collect
, that register is overwritten, and there is no longer any reference to the object, it gets collected.
In this simple example, we are doing nothing with c afterwards. But there is a real case of trouble happening to someone here. They are passing the class reference into a C function that registers the class in a place the GC can't see. The GC collects that information, and by the time that C library uses that (all before main
exits), the data is invalid.
I feel like this might not necessarily be an issue, because technically, you aren't using c
any more, so it can be deallocated immediately. But right in our documentation here it lists ways to alleviate this:
If pointers to D garbage collector allocated memory are passed to C functions, it's critical to ensure that the memory will not be collected by the garbage collector before the C function is done with it. This is accomplished by:
* Making a copy of the data using core.stdc.stdlib.malloc() and passing the copy instead.
* Leaving a pointer to it on the stack (as a parameter or automatic variable), as the garbage collector will scan the stack.
* Leaving a pointer to it in the static data segment, as the garbage collector will scan the static data segment.
* Registering the pointer with the garbage collector with the std.gc.addRoot() or std.gc.addRange() calls.
This to me seems like "leaving a pointer to it on the stack". I'm not sure how else I would do that specifically? Plus, this option is the only "free" one -- the others all require much more complication. Adding a pointer to the stack is free. It's just, I don't know how to tell the compiler to do that besides declaring it.
Note that I don't think any other data types allocated on the heap do this. I tried to change C to a struct, and it is not collected. Change the line to auto c = [new C]
and it works fine.
It's just class references that the compiler seems to not care about ensuring stack references stay alive.
Should it be this way?
-Steve