Hi Christoph,

On Sat, Sep 28, 2024 at 4:47 PM Christoph M. Becker <cmbecke...@gmx.de> wrote:
> > Besides closing resources and killing processes I've seen them store
> > data to disk for caching, remove temp files, call callbacks/dispatch
> > events, change state on other objects, dump stored errors to error_log
> > in a loop in an error handler...
>
> Okay.  My point is that you cannot know (unless there are no circular
> dependencies) *when* a destructor is called by the engine; it may be
> called during some GC run, or during the request shutdown sequence.  As
> it's now, that happens pretty early during shutdown, but that *might*
> change when stream resources are converted to objects.  So you cannot be
> absolutely sure that everything works as expected in destructors.  This
> is a general issue for garbage collected languages; some of these have
> no destructors at all, for such reasons.

I agree. To expand on this, I think that the use of destructors should
be discouraged for many reasons:

 1. Objects with destructors slow down garbage collection due to
potential resurrections. It's not possible to detect resurrections, so
in the presence of destructors the GC has to forget most of its state,
run destructors, and restart.
 2. They may be called in undefined order during GC and shutdown,
including after their dependencies have already been destroyed
 3. They may cause concurrency issues (even in non-concurrent
programs) because they can be called at any time by the GC
 4. They give a false sense of security as destructors will not be
called in case of fatal errors or crashes
 5. Using them to manage non-memory resources (such as file
descriptors, temp files, ...) is not a good idea because the GC is
only triggered by memory metrics. The process may run out of file
descriptors before it triggers a garbage collection, for example.

Part of these problems would be solved by disabling the GC, but
ensuring that large code bases are free of cycles is not an easy task.

Java has deprecated destructors (finalizers) [2] and recommends using
Cleaners [1] instead. Cleaners would resolve the 1st issue, and
partially the 2nd one (for GC, not shutdown), but I'm not entirely
sure they are transposable. Ruby's ObjectSpace.define_finalizer is
similar to Cleaners, AFAIK, in that the finalizer must not reference
the object.

Some use-cases of destructors could be replaced with patterns like
Python's with() [3], Java's try-with [4], or Go's defer [5].

[1] https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Cleaner.html
[2] https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#finalize--
[3] https://docs.python.org/3/reference/compound_stmts.html#with
[4] 
https://docs.oracle.com/javase/8/docs/technotes/guides/language/try-with-resources.html
[5] https://go.dev/doc/effective_go#defer

Best Regards,
Arnaud

Reply via email to