views:

194

answers:

6

Effective Java says :

There is a severe performance penalty for using finalizers.

Why is it slower to destroy an object using the finalizers?

+1  A: 

My thought is this: Java is a garbage collected language, which deallocates memory based on its own internal algorithms. Every so often, the GC scans the heap, determines which objects are no longer referenced, and de-allocates the memory. A finalizer interrupts this and forces the deallocation of memory outside of the GC cycle, potentially causing inefficiencies. I think best practices are to use finalizers only when ABSOLUTELY necessary such as freeing file handles or closing DB connections which should be done deterministically.

Jeremy
Does it really force it, or merely suggest?
glowcoder
Mostly right, but finalizers don't cause memory deallocation outside a GC cycle. Instead, if the GC determines that an object needs to be finalized, it "resurrects" it and keeps the object from being collected until the finalizer has been executed. But it may take a while, since (IIRC) finalizers aren't run until the next time the tenured generation is collected.
Daniel Pryden
"I think best practices are to use finalizers only when ABSOLUTELY necessary such as freeing file handles or closing DB connections": Note that this is precisely where finalizers are *not* appropriate, because the finalizer may run arbitrarily late or not at all.
sleske
A: 

One reason I can think of is that explicit memory cleanup is unnecessary if your resources are all Java Objects, and not native code.

Amir Afghani
+11  A: 

Because of the way the garbage collector works. For performance, most Java GCs use a copying collector, where short-lived objects are allocated into an "eden" block of memory, and when the it's time for that generation of objects to be collected, the GC just needs to copy the objects that are still "alive" to a more permanent storage space, and then it can wipe (free) the entire "eden" memory block at once. This is efficient because most Java code will create many thousands of instances of objects (boxed primitives, temporary arrays, etc.) with lifetimes of only a few seconds.

When you have finalizers in the mix, though, the GC can't simply wipe an entire generation at once. Instead, it needs to figure out all the objects in that generation that need to be finalized, and queue them on a thread that actually executes the finalizers. In the meantime, the GC can't finish cleaning up the objects efficiently. So it either has to keep them alive longer than they should be, or it has to delay collecting other objects, or both. Plus you have the arbitrary wait time of actually executing the finalizers.

All these factors add up to a significant runtime penalty, which is why deterministic finalization (using a close() method or similar to explicitly finalize the object's state) is usually preferred.

Daniel Pryden
This, of course, focuses on issues with a generational collector. Other GC strategies have different issues. But they all boil down to the GC needing to perform extra work, including at least two passes across an object in order to free it; one to add it to the finalize queue, and one to actually free it after finalization.
Software Monkey
+1  A: 

I just picked up my copy Effective Java off my desk to see what he's referring to.

If you read Chapter 2, Section 6, he goes into good detail about the various performance hits.

You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

I would recommend reading the entirety of the section - it explains things much better than I can parrot here.

glowcoder
+1  A: 

If you read the documentation of finalize() closely, you will notice that finalizers enable an object to prevent being collected by the GC.

If no finalizer is present, the object simply can be removed and does not need any more attention. But if there is a finalizer, it needs to be checked afterwards, if the object didn't become "visible" again.

Without knowing exactly how the current Java garbage collection is implemented (actually, because there are different Java implementations out there, there are also different GCs), you can assume that the GC has to do some additional work if an object has a finalizer, because of this feature.

Simon Lehmann
+4  A: 

Having actually run into one such problem:

In the Sun HotSpot JVM, finalizers are processed on a thread that is given a fixed, low priority. In a high-load application, it's easy to create finalization-required objects faster than the low-priority finalization thread can process them. Meanwhile, the space on the heap used by the finalization-pending objects is unavailable for other uses. Eventually, your application may spend all of its time garbage collecting, because all of the available memory is in use by objects pending finalization.

This is, of course, in addition to the other many reasons to not use finalizers that are described in Effective Java.

Sbodd