Don't expect class destructors to be called at all by the GC

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Dec 28 21:29:23 UTC 2017


On Thursday, December 28, 2017 10:37:09 H. S. Teoh via Digitalmars-d-learn 
wrote:
> On Sun, Dec 24, 2017 at 02:07:26PM -0700, Jonathan M Davis via
> Digitalmars-d-learn wrote: [...]
>
> > Regardless, even if it were the case that it were guaranteed that all
> > finalizers were run when the program exited, it would still be
> > terrible practice to rely on it. It's trivial to end up in a situation
> > where no collection is run for quite some time (e.g. just don't do
> > much memory allocation for a while), which would leave any resources
> > that needed to be freed by a finalizer unfreed for quite a while even
> > though they weren't needed anymore. So practically speaking, it
> > doesn't really matter where the finalizers are guaranteed to run.
> > Relying on them to be run rather than forcing them to be run via
> > destroy or using some other helper function is just going to cause
> > problems, so it's just plain bad practice to rely on finalizers to be
> > run to release resources. That's just life with GC's in general, not
> > just D's. It's why C# has the while dispose/IDisposable thing, and why
> > D code should either be using destroy to deal with freeing resources
> > for a class or using structs on the stack for resources that need to
> > be freed. Or alternate memory strategies can be used via
> > std.experimental.allocator. The GC works great for lots of stuff but
> > not for system resources. Honestly, in some ways, we'd be better off
> > if D didn't even have finalizers.
>
> [...]
>
> This makes me wonder if a better approach to memory management is to use
> refcounting by default, and fallback to the GC to collect cycles.
>
> In my current project, I'll probably end up having to use
> RefCounted!Database just so I can have deterministic releasing of
> database handles without needing to worry about dangling references that
> may still be lingering around. (Currently, I'm just calling .destroy
> directly on the handle when I'm done with it, but there's a slim
> possibility that there might be dangling references left somewhere. So
> that needs to be addressed at some point.)

The GC works perfectly well for most things. You just have to be aware of
its downsides - and that includes being aware of what's going to happen if
you try and use it to manage system resources. For that, ref-counting is
almost certainly going to be better (though there are probably cases where
it doesn't matter, because it's not really a problem if the resources aren't
freed before the program exits).

In any case, for a lot of the cases where dispose/IDisposable would be used
in C#, ref-counting is almost certainly what the code should be doing
(either that or scope statements if what's being done is localized and not
common enough to create a type to use with RAII). That's probably on the
list of things that needs to be written up somewhere as being among best
practices for D. There are probably too many things like that that are
sitting in the heads of a lot of folks but not necessarily well communicated
to others - though this particular issue may have been discussed in the
recent GC articles. I don't know. I still need to read them.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list