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

Reply via email to