October 23, 2020 Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Not sure if this is expected behaviour, but I find this weird: The destructor is called on the object just because it gets out of scope? I expect that either GC will not touch the object or only if no reference is on the object anymore. If the object is used in the loop, this will not happen but the GC or compiler should not decide to kill the object if a valid pointer is still in current scope? Even this is a "intelligent" feature by the compiler for non reachable code like that loop, it's still confusing. There are maybe situations while the object should stay in background, eg. socket related/event stuff. If there is a valid pointer, there is no excuse to reap it. class Foo { ~this() { writefln("Warning: destructor called (%s) on %s", typeid(this), this.__vptr); } } class Bar { static Foo create() { return new Foo(); } } void main() { auto foo = Bar.create(); writefln("address: %s", foo.__vptr); while(true) { Thread.sleep(1.seconds); GC.collect(); } } // DMD32 v2.094.1 -m64 on windows |
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame Attachments:
| On Fri, Oct 23, 2020 at 8:20 AM frame via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> Even this is a "intelligent"
> feature by the compiler for non reachable code like that loop,
> it's still confusing. There are maybe situations while the object
> should stay in background, eg. socket related/event stuff. If
> there is a valid pointer, there is no excuse to reap it.
>
>
There is no use of foo anywhere so there is no reason to not collect it. You even does not need to have while in your code:
void main() {
auto foo = Bar.create();
writefln("address: %s", foo.__vptr);
GC.collect();
Thread.sleep(1.seconds);
//foo.sayHello(); // if uncommented GC would not call destructor until
end of main
}
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Kozak | On Friday, 23 October 2020 at 06:37:43 UTC, Daniel Kozak wrote:
> On Fri, Oct 23, 2020 at 8:20 AM frame via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>
>> Even this is a "intelligent"
>> feature by the compiler for non reachable code like that loop,
>> it's still confusing. There are maybe situations while the object
>> should stay in background, eg. socket related/event stuff. If
>> there is a valid pointer, there is no excuse to reap it.
>>
>>
> There is no use of foo anywhere so there is no reason to not collect it.
It is in use until the end of main. Think locking. Seems like the pointer is optimized away and then the GC collects. Makes RAII useless with GC.
Basically a consequence of the optimizer assuming that there is no GC. A rather serious correctness issue.
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On Friday, 23 October 2020 at 06:15:32 UTC, frame wrote:
> Not sure if this is expected behaviour, but I find this weird:
> The destructor is called on the object just because it gets out of scope?
The destructor may not be called at all by the GC, or it might be called whenever there is no pointer found in memory/registers. Since D is essentially using C-style code-gen/optimizers that has little to no knowledge of a GC it may be called prematurely or never.
So basically, destructors do not play well with GC. I've in the past argued that one should not allow destructors on GC objects since there are no proper guarantees anyway. That could also speed up collection. But people prefer convenience over correctness...
IMO, rule of thumb: do not rely on destructors being called at the appropriate time for objects on the GC heap. Do explicit finalisation instead.
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Friday, 23 October 2020 at 07:32:02 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 06:15:32 UTC, frame wrote:
>> Not sure if this is expected behaviour, but I find this weird:
>> The destructor is called on the object just because it gets out of scope?
>
> The destructor may not be called at all by the GC, or it might be called whenever there is no pointer found in memory/registers. Since D is essentially using C-style code-gen/optimizers that has little to no knowledge of a GC it may be called prematurely or never.
>
Simple fix:
auto foo = Bar.create();
=>
scope foo = Bar.create();
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel N | On Friday, 23 October 2020 at 07:39:37 UTC, Daniel N wrote:
> On Friday, 23 October 2020 at 07:32:02 UTC, Ola Fosheim Grøstad wrote:
>> On Friday, 23 October 2020 at 06:15:32 UTC, frame wrote:
>>> Not sure if this is expected behaviour, but I find this weird:
>>> The destructor is called on the object just because it gets out of scope?
>>
>> The destructor may not be called at all by the GC, or it might be called whenever there is no pointer found in memory/registers. Since D is essentially using C-style code-gen/optimizers that has little to no knowledge of a GC it may be called prematurely or never.
>>
>
> Simple fix:
> auto foo = Bar.create();
> =>
> scope foo = Bar.create();
Does not work with LDC -O3:
void main() {
scope foo = Bar.create();
GC.collect();
writefln("Collection done");
exit(0);
}
Output:
Warning: destructor called (onlineapp.Foo) on 55A899C1C570
Collection done
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Friday, 23 October 2020 at 08:32:45 UTC, Ola Fosheim Grøstad wrote:
> Does not work with LDC -O3:
>
> void main() {
> scope foo = Bar.create();
> GC.collect();
> writefln("Collection done");
> exit(0);
> }
>
> Output:
>
> Warning: destructor called (onlineapp.Foo) on 55A899C1C570
> Collection done
Wow I couldn't believe 'scope' didn't work because it's used a lot for this same purpose.
But after checking DMD sources I realized that 'scope' works only if it precedes a new expression initializer (scope var = new ClassName...) which makes sense but it's really confusing.
That means in OP example 'scope' does nothing, it's just like 'auto' and the class is heap allocated.
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Boris Carvajal | On Friday, 23 October 2020 at 12:53:52 UTC, Boris Carvajal wrote:
> That means in OP example 'scope' does nothing, it's just like 'auto' and the class is heap allocated.
Hm. I think what happens is that exit() makes the destructor call unreachable therefore the pointer to the object is not saved on the stack and not retained in any registers. Thus GC.collect finds no references to it.
It works if you remove exit().
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ola Fosheim Grøstad | On Friday, 23 October 2020 at 13:11:49 UTC, Ola Fosheim Grøstad wrote:
> On Friday, 23 October 2020 at 12:53:52 UTC, Boris Carvajal wrote:
>> That means in OP example 'scope' does nothing, it's just like 'auto' and the class is heap allocated.
>
> Hm. I think what happens is that exit() makes the destructor call unreachable therefore the pointer to the object is not saved on the stack and not retained in any registers. Thus GC.collect finds no references to it.
>
> It works if you remove exit().
exit() is irrelevant, I checked the asm output.
|
October 23, 2020 Re: Destructor called while object is still alive | ||||
---|---|---|---|---|
| ||||
Posted in reply to Boris Carvajal | On Friday, 23 October 2020 at 13:23:18 UTC, Boris Carvajal wrote:
> exit() is irrelevant, I checked the asm output.
Ok, I guess GC.collect() is called after main() by the runtime then.
|
Copyright © 1999-2021 by the D Language Foundation