Why do finalizers have a "severe performance penalty"?
Asked Answered
B

6

27

Effective Java says :

There is a severe performance penalty for using finalizers.

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

Banas answered 18/5, 2010 at 18:52 Comment(1)
You may like this article, it talks about how finalizers can make objects reachable again, etc. It also shows why composition can save the day (instead of implementation inheritance) in quite some cases: java.sun.com/developer/technicalArticles/javase/finalizationBacciferous
N
23

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.

Noemi answered 18/5, 2010 at 19:0 Comment(4)
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.Duala
Would I be correct in thinking that several commonly used Java API classes have finalizers, to free O/S resources? I'm thinking of FileOutputStream. So it is unlikely that finalizers for some objects will delay GC of objects that don't use finalizers, because most programs would be affected.Palfrey
@Raedwald: Right. For example, the OpenJDK implementation of FileOutputStream has a finalizer, which you can see by looking at the OpenJDK source. (I'm not able to find anything that requires the standard library implementation to use finalizers, though.) So in practice, objects that are otherwise eligible for GC but that are still pending finalization are just promoted to the next older generation (survivor space or tenured) while the finalizer is queued to run. But the actual memory won't be reclaimed until the next time that next older generation is collected.Noemi
Assuming an object that has both, close() and finalize() implementations, does this overhead also happens if we called close() explicitly?Avar
P
10

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.

Pyorrhea answered 18/5, 2010 at 19:6 Comment(0)
I
2

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.

Infallible answered 18/5, 2010 at 19:2 Comment(0)
H
2

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.

Haberdashery answered 18/5, 2010 at 19:3 Comment(1)
Indeed, this page also mentions that the JVM treats objects with non-trivial finalizers differently: fasterj.com/articles/finalizer2.shtmlAvar
T
1

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.

Tilburg answered 18/5, 2010 at 18:57 Comment(3)
Does it really force it, or merely suggest?Infallible
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.Noemi
"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.Ineffable
U
0

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

Unpremeditated answered 18/5, 2010 at 18:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.