Why is finalize wrong?
Stanimir Simeonoff
stanimir at riflexo.com
Wed Sep 3 00:15:12 UTC 2014
Hi Jaroslav,
In my opinion the greatest downside of the finalize() is that involves
enlisting in a shared queue prior to the object creation. The queue is
shared amongst the entire VM and that could be a potential bottleneck.
In practical terms having a finalize method involves a full memory fence
when a new instance is created (or cloned) and removing a finalize() method
can break some racy code.
Like David Lloyd mentioned finalize() can be invoked concurrently to some
methods (if there is no reachability to 'this'). OTOH I see finalize useful
for managing native resources mostly and possibly stopping (signaling)
threads left unmanaged. In that particular case synchronization is a must,
so it comes naturally.
Personally I never use dedicated threads with ReferenceQueue but I poll
(expunge) the queue on add/put. If the there are too many successful
ReferenceQueue.poll() hijacking the Finalizer thread is an option.
Something among the lines:
public static interface Cleaner<T, Ref extends Reference<? extends T>>{
void clean(Ref ref);
}
public static <T, Ref extends Reference<? extends T>> void
expunge(Cleaner<T, Ref> cleaner, final ReferenceQueue<T> queue, int
expungeThreshold){
Reference<? extends T> w=queue.poll();
if (w==null)
return;//keep the method short, so it's inlined; queue being empty is
the fast lane
expungeImpl(cleaner, w, queue, expungeThreshold);
}
static <T, Ref extends Reference<? extends T>> void expungeImpl(final
Cleaner<T, Ref> cleaner, Reference<? extends T> ref, final
ReferenceQueue<T> queue, int expungeThreshold){
expunge(cleaner, ref);
for (;--expungeThreshold>0 && null!=(ref=queue.poll());){
expunge(cleaner, ref);
}
if (expungeThreshold==0){//delegate a full expunge to the finalizer
//the anon. class stores cleaner and queue in fields, and the c-tor
has happens before with finalize() unlike any other method
new Object(){//feel free to extend/wrap WeakRefence(cleaner) and also
keep weak ref to queue useful for non-native resources
protected void finalize(){//keep in mind: the execution of
cleaner.clean(Ref) will not have the right ContextClassLoader, ThreadGroup,
AccessControlContext
expunge(cleaner, queue, Integer.MAX_VALUE);
}
};
}
}
@SuppressWarnings("unchecked")
private static <T, Ref extends Reference<? extends T>> void
expunge(Cleaner<T, Ref> cleaner, Reference<? extends T> ref){
cleaner.clean((Ref) ref);
}
Some colorization:
http://pastebin.com/1V9Jhavz
Of course the finalize method is not guaranteed to execute at all but if
expungeThreshold is greater than 1 and expunge is called on each add/put
operation, still it should be ok.
Cheers
Stanimir
On Mon, Aug 4, 2014 at 4:46 PM, Jaroslav Tulach <jaroslav.tulach at oracle.com>
wrote:
> Hi.
> Last week we touched topic of finalization and what is wrong with it. I
> proposed three
> reasons why Object.finalize is bad. Is it correct and extensive list or
> would you change
> it or expand it? Thanks as ...
>
> > > # 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?
>
> ...I consider #2 the worst idea:
>
> > > 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
>
> and I think I got some support...
>
> > 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.
>
> ...I just have to say that the Reference&ReferenceQueue as of JDK8 is not
> enough. As
> the case of activeReferenceQueue shows, the fact that each instance of
> ReferenceQueue where one uses remove() needs one dedicated thread is a big
> flaw.
> Fixing the flaw in an external library is not easy (see the summary from
> last week:
>
> https://bugs.openjdk.java.net/browse/JDK-8051843?focusedCommentId=13531670&p
> [1]age=com.atlassian.jira.plugin.system.issuetabpanels:comment-
> tabpanel#comment-13531670).
>
> Moreover David Holmes finds introductions of new system level threads
> uneasy:
>
> > 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.
>
> But when thinking about it, we can have functionality of
> activeReferenceQueue in JDK
> without introducing any new thread! We can reuse the existing finalizer
> one!
>
> I plan to propose a patch which will look at the activeReferenceQueue
> problem from a
> completely new perspective and offer "lightweight finalize" solution that
> will have
> above discussed properties #1 and #3, but will not suffer from #2 (the
> biggest
> problem with Object.finalize).
>
> I just wanted to ask before I start: Is somebody aware of other problems
> with
> Object.finalize than #1, #2 and #3?
> -jt
>
>
>
> --------
> [1]
>
> https://bugs.openjdk.java.net/browse/JDK-8051843?focusedCommentId=13531670&p
>
More information about the core-libs-dev
mailing list