Performance implications of finalizers on JVM
Asked Answered
L

4

7

According to this post, in .Net,

Finalizers are actually even worse than that. Besides that they run late (which is indeed a serious problem for many kinds of resources), they are also less powerful because they can only perform a subset of the operations allowed in a destructor (e.g., a finalizer cannot reliably use other objects, whereas a destructor can), and even when writing in that subset finalizers are extremely difficult to write correctly. And collecting finalizable objects is expensive: Each finalizable object, and the potentially huge graph of objects reachable from it, is promoted to the next GC generation, which makes it more expensive to collect by some large multiple.

Does this also apply to JVMs in general and to HotSpot in particular?

Lavender answered 2/6, 2010 at 4:7 Comment(1)
altough not entirely related (hence the comment and not an answer), here's a great and not too old (2007) article on potential finalization issues java.sun.com/developer/technicalArticles/javase/finalization As a bonus, the article shows yet another case where using implementation inheritance instead of composition leads to issues where you'd least expect them.Ethnography
C
3

Here are some select quotes from Effective Java 2nd Edition: Item 7: Avoid finalizers:

Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have few valid uses, [...] as a rule of thumb, you should avoid finalizers.

You should really make sure that in fact, you DO need finalizers; most of the time you DON'T.

C++ programmers are cautioned not to think of finalizers as Java's analog of C++ destructors. In C++, destructors are the normal way to reclaim the resources associated with an object, a necessary counterpart to constructors. In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer. C++ destructors are also used to reclaim other nonmemory resources. In Java, the try-finally block is generally used for this purpose.

The semantics of when finalizers are invoked is also important to consider:

JLS 12.6 Finalization of Class Instances

The Java programming language does not specify how soon a finalizer will be invoked [...nor] which thread will invoke the finalizer for any given object. [...] If an uncaught exception is thrown during the finalization, the exception is ignored and finalization of that object terminates. (JLS 12.6.2) Finalizer Invocations are Not Ordered

Moreover, the only mechanism to run finalizer on demand is broken. The following quotes are from Effective Java 2nd Edition:

[...] The only methods that claim to guarantee finalization are System.runFinalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated.

Bloch went further to comment on the performance penalty (emphasis his):

Oh, and there's one more thing: there is a severe performance penalty for using finalizers. On my machine, the time to create and destroy a simple object is about 5.6ns. Adding a finalizer increases the time to 2,400ns. In other words, it is about 430 times slower to create and destroy objects with finalizers.

With such little details on the benchmarking methodology, I don't think the specific numbers mean much, but it does confirm what have been widely documented: finalizers are very costly.

The book does explain the rare scenarios under which using finalizers is valid. The omission of those quotes from this answer is intentional.

Coddle answered 2/6, 2010 at 7:31 Comment(1)
A question about these numbers and the reasons behind the costs has been brought up later, here is the explanation. The answer to whether this applies to all JVMs is simple: since it requires a JVM which detects classes which do not override finalize(), to optimize these cases, JVMs not having such an optimization would treat all objects the same way, as slow as if they have a custom finalize() method. But any reasonable JVM has this optimization (or doesn’t support finalization in the first place).Resuscitate
L
4

Here is an explicit statement from 2004:

Objects with finalizers (those that have a non-trivial finalize() method) have significant overhead compared to objects without finalizers, and should be used sparingly. Finalizeable objects are both slower to allocate and slower to collect. At allocation time, the JVM must register any finalizeable objects with the garbage collector, and (at least in the HotSpot JVM implementation) finalizeable objects must follow a slower allocation path than most other objects. Similarly, finalizeable objects are slower to collect, too. It takes at least two garbage collection cycles (in the best case) before a finalizeable object can be reclaimed, and the garbage collector has to do extra work to invoke the finalizer. The result is more time spent allocating and collecting objects and more pressure on the garbage collector, because the memory used by unreachable finalizeable objects is retained longer. Combine that with the fact that finalizers are not guaranteed to run in any predictable timeframe, or even at all, and you can see that there are relatively few situations for which finalization is the right tool to use.

Lavender answered 2/6, 2010 at 5:14 Comment(0)
C
3

Here are some select quotes from Effective Java 2nd Edition: Item 7: Avoid finalizers:

Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have few valid uses, [...] as a rule of thumb, you should avoid finalizers.

You should really make sure that in fact, you DO need finalizers; most of the time you DON'T.

C++ programmers are cautioned not to think of finalizers as Java's analog of C++ destructors. In C++, destructors are the normal way to reclaim the resources associated with an object, a necessary counterpart to constructors. In Java, the garbage collector reclaims the storage associated with an object when it becomes unreachable, requiring no special effort on the part of the programmer. C++ destructors are also used to reclaim other nonmemory resources. In Java, the try-finally block is generally used for this purpose.

The semantics of when finalizers are invoked is also important to consider:

JLS 12.6 Finalization of Class Instances

The Java programming language does not specify how soon a finalizer will be invoked [...nor] which thread will invoke the finalizer for any given object. [...] If an uncaught exception is thrown during the finalization, the exception is ignored and finalization of that object terminates. (JLS 12.6.2) Finalizer Invocations are Not Ordered

Moreover, the only mechanism to run finalizer on demand is broken. The following quotes are from Effective Java 2nd Edition:

[...] The only methods that claim to guarantee finalization are System.runFinalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated.

Bloch went further to comment on the performance penalty (emphasis his):

Oh, and there's one more thing: there is a severe performance penalty for using finalizers. On my machine, the time to create and destroy a simple object is about 5.6ns. Adding a finalizer increases the time to 2,400ns. In other words, it is about 430 times slower to create and destroy objects with finalizers.

With such little details on the benchmarking methodology, I don't think the specific numbers mean much, but it does confirm what have been widely documented: finalizers are very costly.

The book does explain the rare scenarios under which using finalizers is valid. The omission of those quotes from this answer is intentional.

Coddle answered 2/6, 2010 at 7:31 Comment(1)
A question about these numbers and the reasons behind the costs has been brought up later, here is the explanation. The answer to whether this applies to all JVMs is simple: since it requires a JVM which detects classes which do not override finalize(), to optimize these cases, JVMs not having such an optimization would treat all objects the same way, as slow as if they have a custom finalize() method. But any reasonable JVM has this optimization (or doesn’t support finalization in the first place).Resuscitate
G
1

Java has a very similar mechanism for finalizers. Here is a good article on finalizers: http://www.javaworld.com/javaworld/jw-06-1998/jw-06-techniques.html

The general rule of thumb in Java is don't use them.

Gadmon answered 2/6, 2010 at 4:31 Comment(6)
@Romain Hippeau: gotta love the 12 years old article linked there :)Ethnography
Actually, I am interested precisely in the use case the article considers acceptable: "But you may also wish to include a finalizer that checks to make sure the resource has already been released, and if it hasn't, that goes ahead and releases it. Such a finalizer guards against (and hopefully will not encourage) sloppy use of your class."Lavender
@Alexey - if you really need to use finalizers for something, then you need to use them even if they are rather expensive. They are not THAT expensive ... provided that you don't create thousands of finalizable objects.Aceto
@ Stephen C. If you really need to use finalizers for something, then you need to do something else, because there is no guarantee that the finalizer will be run at all.Tautog
@Ethnography In a few years it will be a twenty year old article :(Gadmon
@Alexey I get the impression you are trying to justify using them, It is not just from a performance point of view that they are bad, but also if an exception gets thrown in the finalizer then it will never get called. It is also possible to create memory leaks from inside them. DO NOT USE THEM !!!! Use the try/finally language construct instead.Gadmon
V
0

finalize() Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.

Don´t use Finalizers, mainly because are unpredictable and we don´t know when will be executed, "don't try to be smarter than the JVM"

Vercelli answered 30/1, 2014 at 18:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.