[concurrency-interest] Object finalization
hans.boehm at hp.com
Tue May 15 19:45:13 EDT 2012
> From: Gregg Wonderly
> On 5/15/2012 8:15 AM, David M. Lloyd wrote:
> > I've given up on finalization for native resources for just this
> reason; instead
> > I use refcounting. It's not great because there's a CAS on entry+exit
> but I find
> > that I can also use the same trick to replace locking in many cases
> so I'm
> > hoping that it'll even out in the benchmarks. I almost have myself
> > that it will scale better than the finalizer option. We shall see.
> I've done this in a lot of other places myself too. It just simplifies
> so many
> things to have dependable, accurate life cycles.
It seems to me that, depending on the context, that may be trading one can of worms for another.
Possibly a bigger one.
To me, there are two distinct kinds of cleanup actions:
1) Logically synchronous; the programmer knows exactly where it's happening, and can make
sure that the locks acquired by the cleanup code aren't acquired in the wrong order with
respect to the calling context, etc. C++ destructors are good at this. Explicit cleanup
calls often aren't too bad. Finalizers are terrible. It seems to me that this case usually
doesn't even need reference counting, though occasionally it might. And that may be a perfectly
good use for reference counting.
2) Logically asynchronous. We want it to be cleaned up anytime before we run out of the resource,
whenever that may be. This is what happens when an assignment drops the last reference to a
large data structure containing library-defined objects which I don't understand, and have
no business understanding. Finalizers and java.lang.ref are potentially tolerable at this,
with some fixes. Traditional reference counting with synchronous resource reclamation
is terrible at it (at least in the absence of transactional memory). C++ destructors have
the same problem. The problem is that a completely innocuous looking call, performing a
reference-counted assignment, may end up acquiring a huge collection of locks I don't know
about, making it essentially impossible to reliably avoid deadlocks. (This is essentially identical
to the JDK 1.0.2 finalization bug misdescribed as a language design bug as the first
item in http://www.cs.arizona.edu/projects/sumatra/hallofshame/ ) The problem can be avoided
by queuing cleanups to be run later in a separate thread, just like finalizers.
It also seems to me that using reference counting for (2) has a tendency to infect lots of things
with reference counting, and may largely make the GC redundant. Once we do that, we've solved a
problem that was solvable with perhaps a 1% optimization cost by adding huge overhead to every
pointer assignment. (Not to mention garbage cycles.)
These issues are described in more detail in http://dl.acm.org/citation.cfm?doid=604131.604153 .
More information about the Concurrency-interest