On Tue, 10 Aug 2010 08:27:19 -0400, bearophile <[email protected]>
wrote:
Andrei Alexandrescu:
class File {
FILE * fp;
this() { fp = fopen("/tmp/temporary"); }
~this() { fclose(fp); }
}
I am not expert enough about all this topic, so I try to keep myself out
of this discussion. Recently I have read this:
issues with finalizers. The main problem with finalizers and garbage
collection is that finalizers generally try to reclaim a non-memory
resource (such as a specific file, a file handle, or a network socket)
based purely on a memory reclamation policy (generally triggered by
later memory allocations). Generally those other resource exhaust well
before memory does, then you're out of the resource stuck waiting for
garbage collection to notice a particular finalizer needs to be run. A
better general rule of thumb is "don't ever use finalizers". Instead
always call close inside a finally.<
This means that putting something like a fclose(fp) inside the ~this()
is wrong, you need to add a specific method to that File class to ask
for the closing of the file, because you generally can't rely on the GC
for this, because a GC is free to even never collect objects, if there
is enough RAM.
In my opinion it's correct to put something like a fclose(fp) inside the
~this() only if you are sure this struct/class will be always allocated
on the stack, so its destructor will always be called deterministically
when the scope ends (a reference counting strategy too seems acceptable,
because it is deterministic).
In a GC-based language you can't assume your destructors are run, so
your destructors usually need to be empty, and you need to add other
methods to free explicitly and manually (or with a scope(exit)) all the
resources that aren't RAM.
Please take this cum grano salis, I am not an expert on this.
destructors are for the purpose of clearing out resources that are not
provided by the GC. Not having it in the destructor is a bad thing.
The thing is, if you *don't* call it in the destructor, and require
someone to manually call your explicit method, then someone will forget to
make that call, and your resource stays open forever. It also means you
have to call your method when going out of scope.
By clearing that resource in the destructor, and allowing a way to
manually call that destructor, the class works in all three situations:
manual resource management (via clear), scoped destruction, and GC
destruction.
I think in time, the GC may be associated with it's own thread, and may
run on a schedule vs. having to wait for a memory allocation to run. When
this happens, it's more likely you can rely on the GC to free the
resources in a reasonable amount of time.
-Steve