Don't expect class destructors to be called at all by the GC
Mike Parker
aldacron at gmail.com
Thu Dec 21 06:50:44 UTC 2017
On Thursday, 21 December 2017 at 04:10:56 UTC, user1234 wrote:
> On Thursday, 21 December 2017 at 02:57:00 UTC, Mike Franklin
>> Unfortunately, that doesn't really shed much light on this
>> oddity. So, specifically, under what circumstances are
>> destructors not called?
>>
>
> When the GC is unaware of a class instance (an "unreferenced
> object") it won't call its destructor. This happens when you
> use alternative memory management strategy, for example using
> Mallocator and make you get an unreferenced object that you
> have to manage yourself.
The root of the problem is that in D, class destruction and
finalization are conflated. It would be much more accurate to
refer to ~this in classes as a finalizer. Then this sort of
confusion wouldn't be so widespread.
Also, consider the current GC implementation finalizes any
objects that haven't yet been finalized when it terminates. It
terminates during runtime termination, but *after* static
destructors are executed (which is how it should be). We already
know that you can't rely on any GC memory references to be valid
in a class destructor, but because of this cleanup phase, you
also can't rely on any program state still being valid.
As an example, each of the Derelict packages used to
(pointlessly) unload its shared library in a static destructor,
but people repeatedly had segfaults at app exit because their
class destructors were calling into the loaded libraries to
release resources. The solution there was easy -- stop manually
unloading the libraries and let the OS do it at process
termination. But anyone who wants to unload any resources in a
class destructor needs to be aware of this issue in case a static
destructor somewhere is getting to it first. And I still see code
using Derelict where people unload the library themselves in a
static destructor.
I just don't even bother with class destructors. Without a
guarantee that they can run and without any sort of deterministic
behavior, it's really not appropriate to refer to them as
destructors and they're about as useful as Java finalizers, which
means not at all. In order to make them less error prone, we need
to separate the concept of destruction from finalization and
allow both destructors and finalizers. That's what I've taken to
doing manually, by implementing a `terminate` function in my
classes that I either call directly or via a ref-counted
templated struct called Terminator.
More information about the Digitalmars-d-learn
mailing list