Why is finalize wrong? was: Benefits of activeReferenceQueue

David Holmes david.holmes at oracle.com
Tue Jul 29 10:49:25 UTC 2014


Hi Jaroslav,

On 29/07/2014 7:24 PM, Jaroslav Tulach wrote:
> Hello David,
> the following suggestions are hard to argue with as they touch philosophical
> aspects of software engineering. In spite of that I will attempt to analyze
> and argue:

Always happy to get philosophical about software design :)

> Dne Út 29. července 2014 18:16:24, David Holmes napsal(a):
>> I think the fundamental flaw of activeReferenceQueue is in trying to
>> hide the thread management from the user.
>
> I guess this builds on top of bad experience with Object.finalize() method. No
> doubt existence of the method is failure, but why?
>
> # 1 - Because of automatic JDK management thread?
> # 2 - Or because once finalize() is called, one still has reference to the
> "gone" object and can re-activate it?
> #3 - Or because the finalizer thread is shared between completely unrelated
> objects and misbehavior of one can cause problems for others?
>
> My personal top three starts with #2. That is logically invalid. #3 can be a
> problem, but so can be any other misbehaving thread in an application. #1 is
> completely OK, from my perspective (if the thread can GC when necessary).

I think you are conflating different issues that happen to come together 
in this case. finalization is problematic - no doubt about it - it was a 
flawed idea (with good intentions) to get around lack of destructors to 
support C++ style RAII (resource acquisition is initialization) idiom.

Weak references and reference queues were introduced to address some of 
the short comings of finalization as well as providing other 
capabilities within a GC'd environment.

The threading aspect is somewhat tangential. Any time you choose to 
introduce a new thread to perform a task you have to be concerned about 
the lifetime of the task and thus the thread. Telling a thread when it 
should stop a repetitive task is one of the key design points of 
multi-threaded solutions. Automatic threading solutions work fine for 
one-shot tasks, from a lifecycle management perspective, but otherwise 
you either have to expose management methods, or else assume an 
environment when the thread's lifecycle is that of the "application". 
And as per the case at hand the latter is a problem when we have these 
environments that almost, but not quite, provide independent execution 
contexts.

> I'd like to point out that activeReferenceQueue does not have #2 problem at
> all. So in my eyes it behaves fine (once we allow its thread to GC properly).

The GC issue is somewhat incidental - the key issue is "when should this 
thread stop doing what it does?".

>> By automating the threading
>> there is no way to control the lifecycle of the thread and hence we have
>> the problem at hand. When the application code manages the thread
>> itself, then it can also manage its lifecycle and avoid the problem.
>
> Alas, the primary (and only generally available) way to find out that something
> is not used in Java SE is wait when it gets garbage collected, right?

Equating lifecycle with "reachability" is convenient but it is inexact. 
Though you could argue that the problem here is an extension of the 
normal "cyclical garbage" problem - the GC can't know that the 
processing thread in this case is itself "garbage" because nothing can 
submit any more work for it to do.

Maybe that is a solvable problem, but to me, here and now, 
responsibility for shutting down the thread lies with the subsystem that 
created it.

> Application frameworks on top of Java have notion of a lifecycle, but it is
> different for Servlets, for NetBeans Platform, and any other framework. When
> writing general purpose library in Java, GC is the only tool in hand.

Alas just because all you own is a hammer it doesn't turn everything 
into a problem solvable with nails.

The Java platform provides a number of "execution contexts" with 
short-comings in lifecycle management in my opinion (harking back to 
applets).

>> This might be a case where some kind of user-level "reference counting"
>> would be a better solution. But of course that would require changes to
>> all existing users of the class.
>
> We tried some reference counting, but it is not reliable. Of course, all
> problems in software engineering can be solved by yet another level of
> indirection - I can create my own wrapper for WeakReference, ReferenceQueue
> and etc. and then I have everything under my control. But why not slightly
> improve the existing library and make it more flexible?

To modify the existing library requires establishing that the new 
functionality is sufficiently general and "carries its weight" - 
otherwise everyone has an improvement that helps their particular use-case.

Once you've painted yourself into a corner, hanging a rope from the 
ceiling might seem like an obvious solution, but it's better to not 
paint yourself into the corner to begin with.

Cheers,
David

> -jt
>
>



More information about the core-libs-dev mailing list