27

Effective Java says :

There is a severe performance penalty for using finalizers.

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

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
unj2
  • 52,135
  • 87
  • 247
  • 375
  • 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: http://java.sun.com/developer/technicalArticles/javase/finalization/ – SyntaxT3rr0r May 19 '10 at 00:13

6 Answers6

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.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • 1
    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. – Lawrence Dol May 19 '10 at 02:01
  • 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. – Raedwald Aug 01 '14 at 10:52
  • 1
    @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. – Daniel Pryden Aug 01 '14 at 15:09
  • Assuming an object that has both, `close()` and `finalize()` implementations, does this overhead also happens if we called `close()` explicitly? – Gerardo Cauich Jun 26 '21 at 01:08
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.

Sbodd
  • 11,279
  • 6
  • 41
  • 42
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.

corsiKa
  • 81,495
  • 25
  • 153
  • 204
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.

Simon Lehmann
  • 10,737
  • 4
  • 41
  • 53
  • Indeed, this page also mentions that the JVM treats objects with non-trivial finalizers differently: https://www.fasterj.com/articles/finalizer2.shtml – Gerardo Cauich Jun 26 '21 at 10:49
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.

Jeremy
  • 4,808
  • 2
  • 21
  • 24
  • 1
    Does it really force it, or merely suggest? – corsiKa May 18 '10 at 18:58
  • 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 May 18 '10 at 19:04
  • 2
    "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 Oct 04 '10 at 18:47
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.

Amir Afghani
  • 37,814
  • 16
  • 84
  • 124