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