38

The main two arguments against overriding Object.finalize() is that:

  1. You don't get to decide when it's called.

  2. It may not get called at all.

If I understand this correctly, I don't think those are good enough reasons to hate Object.finalize() so much.

  1. It is up to the VM implementation and the GC to determine when the right time to deallocate an object is, not the developer. Why is it important to decide when Object.finalize() gets called?

  2. Normally, and correct me if I'm wrong, the only time Object.finalize() wouldn't get called is when the application got terminated before the GC got a chance to run. However, the object got deallocated anyway when the application's process got terminated with it. So Object.finalize() didn't get called because it was not needed to be called. Why would the developer care?

Every time I'm using objects that I have to manually close (like file handles and connections), I get very frustrated. I have to be constantly checking if an object has an implementation of close(), and I'm sure I have missed a few calls to it at some points in the past. Why isn't it just simpler and safer to just leave it to the VM and GC to dispose of these objects by putting the close() implementation in Object.finalize()?

4
  • 1
    Also note: like many APIs from Java 1.0 era, thread semantics of finalize() is a bit messed up. If you ever implement it, make sure it's thread-safe with respect to all other methods on the same object.
    – billc.cn
    Jul 5, 2015 at 0:40
  • 2
    When you hear people saying finalizers are bad, they don't mean your program will stop working if you have them; they mean the whole idea of finalization is pretty useless.
    – user253751
    Jul 5, 2015 at 3:30
  • 1
    +1 to this question. Most of the answers below state that the resources like file descriptors are limited and that they should be manually collected. The same was/is true about memory so if we accept some delay in memory collection, why not accepting it for file descriptors and/or other resources ?
    – mbonnin
    May 4, 2016 at 18:53
  • Addressing your last paragraph, you can leave it to Java to handle closing things like file handles and connections with very little effort on your part. Use the try-with-resources block - it's mentioned a few other times already in answers and comments, but I think it's worth putting here. The Oracle tutorial for this is found at docs.oracle.com/javase/tutorial/essential/exceptions/…
    – Jeutnarg
    May 26, 2016 at 18:02

8 Answers 8

47

In my experience, there is one and only one reason for overriding Object.finalize(), but it is a very good reason:

To place error logging code in finalize() which notifies you if you ever forget to invoke close().

Static analysis can only catch omissions in trivial usage scenarios, and the compiler warnings mentioned in another answer have such a simplistic view of things that you actually have to disable them in order to get anything non-trivial done. (I have far more warnings enabled than any other programmer that I know of or ever heard of, but I don't have stupid warnings enabled.)

Finalization might seem to be a good mechanism for making sure that resources do not go undisposed, but most people see it in a completely wrong way: they think of it as an alternate fallback mechanism, a "second chance" safeguard which will automagically save the day by disposing of the resources that they forgot. This is dead wrong. There must be only one way of doing any given thing: either you always close everything, or finalization always closes everything. But since finalization is unreliable, finalization cannot be it.

So, there is this scheme which I call Mandatory Disposal, and it stipulates that the programmer is responsible for always explicitly closing everything which implements Closeable or AutoCloseable. (The try-with-resources statement still counts as explicit closing.) Of course, the programmer may forget, so that's where finalization comes into play, but not as a magic fairy which will magically make things right in the end: If finalization discovers that close() was not invoked, it does not attempt to invoke it, precisely because there will (with mathematical certainty) be hordes of n00b programmers who will rely on it to do the job that they were too lazy or too absent minded to do. So, with mandatory disposal, when finalization discovers that close() was not invoked, it logs a bright red error message, telling the programmer with big fat all-capital letters to fix his s-- er, his stuff.

As an additional benefit, rumor has it that "the JVM will ignore a trivial finalize() method (e.g. one which just returns without doing anything, like the one defined in the Object class)", so with mandatory disposal you can avoid all finalization overhead in your entire system (see alip's answer for information on how terrible this overhead is) by coding your finalize() method like this:

@Override
protected void finalize() throws Throwable
{
    if( Global.DEBUG && !closed )
    {
        Log.Error( "FORGOT TO CLOSE THIS!" );
    }
    //super.finalize(); see alip's comment on why this should not be invoked.
}

The idea behind this is that Global.DEBUG is a static final variable whose value is known at compilation time, so if it is false then the compiler will not emit any code at all for the entire if statement, which will make this a trivial (empty) finalizer, which in turn means that your class will be treated as if it does not have a finalizer. (In C# this would be done with a nice #if DEBUG block, but what can we do, this is java, where we pay apparent simplicity in the code with additional overhead in the brain.)

More about Mandatory Disposal, with additional discussion about disposing of resources in dot Net, here: michael.gr: Mandatory disposal vs. the "Dispose-disposing" abomination

29
  • 2
    @MikeNakis Don't forget, Closeable is defined as doing nothing if called a second time: docs.oracle.com/javase/7/docs/api/java/io/Closeable.html . I admit that I've sometimes logged a warning when my classes are closed twice, but technically you're not even supposed to do that. Technically though, calling .close() on Closable more than once is perfectly valid.
    – Patrick M
    Jul 5, 2015 at 15:02
  • 1
    @usr it all boils down to whether you trust your testing or you don't trust your testing. If you don't trust your testing, sure, go ahead and suffer the finalization overhead to also close(), just in case. I believe that if my testing is not to be trusted, then I better not release the system to production.
    – Mike Nakis
    Jul 5, 2015 at 15:36
  • 3
    @Mike, for the if( Global.DEBUG && ... construct to work so the JVM will ignore the finalize() method as trivial, Global.DEBUG must be set at compilation time (as opposed to injected etc.) so what follows will be dead code. The call to super.finalize() outside the if block is also enough for the JVM to treat it as non-trivial (regardless of the value of Global.DEBUG, at least on HotSpot 1.8) even if also the super class's #finalize() is trivial!
    – alip
    Jul 6, 2015 at 14:24
  • 1
    @Mike I'm afraid that's the case. I tested it with (a slightly modified version) of the test in the article you linked to, and verbose GC output (together with surprisingly poor performance) confirm that the objects are copied to survivor/old generation space and need full heap GC to get rid of.
    – alip
    Jul 6, 2015 at 14:55
  • 1
    What is often overlooked, is the risk of earlier-than-expected collection of objects which makes freeing resources in the finalizer a very dangerous action. Prior to Java 9, the only way to be sure that a finalizer doesn’t close the resource while it is still in use, is to synchronize on the object, in both, the finalizer and the method(s) using the resource. That’s why it works in java.io. If that kind of thread safety wasn’t on the wish list, it adds to the overhead induced by finalize()
    – Holger
    Nov 28, 2017 at 9:01
28

Every time I'm using objects that I have to manually close (like file handles and connections), I get very frustrated. [...] Why isn't it just simpler and safer to just leave it to the VM and GC to dispose of these objects by putting the close() implementation in Object.finalize()?

Because file handles & connections (that is, file descriptors on Linux & POSIX systems) are quite a scarce resource (you might be limited to 256 of them on some systems, or to 16384 on some others; see setrlimit(2)). There is no guarantee that the GC will be called enough often (or at the right time) to avoid exhausting such limited resources. And if the GC is not called enough (or the finalization are not run at the right time) you'll be reaching that (possibly low) limit.

Finalization is a "best effort" thing in the JVM. It might not be called, or be called quite late... In particular, if you have a lot of RAM or if your program don't allocate a lot of objects (or if most of them die before being forwarded to some old enough generation by a copying generational GC), the GC could be called quite rarely, and the finalizations might not run very often (or even might not run at all).

So close file descriptors explicitly, if possible. If you are afraid of leaking them, use finalization as an extra measure, not as the main one.

8
  • 7
    I would add that closing a stream to a file or socket normally flushes it. Leaving streams open needlessly increases the risk of data loss if the power goes out, a connection drops (this is also a risk for files accessed across a network), etc.
    – user22815
    Jul 4, 2015 at 21:44
  • 2
    Actually, while the number of filedescriptors allowed might be low, that's not the really sticky point, because one could use that as a signal for the GC at least. The really problematic thing it's a) completely intransparent to the GC how much non-GC-managed resources hang on it, and b) many of those resources are unique, so others will might be blocked or denied. Jul 5, 2015 at 0:59
  • 2
    And if you leave a file open it might interfere with someone else using it. (Windows 8 XPS viewer, I'm looking at you!) Jul 5, 2015 at 2:15
  • 2
    "If you are afraid of leaking [file descriptors], use finalization as an extra measure, not as the main one." This statement sounds fishy to me. If you design your code well, should you really introduce redundant code that spreads cleanup over multiple places?
    – mucaho
    Jul 5, 2015 at 13:17
  • 2
    @BasileStarynkevitch Your point being that in an ideal world, yes, redundancy is bad, but in practice, where you can not foresee all relevant aspects, better be safe than sorry?
    – mucaho
    Jul 6, 2015 at 1:01
13

Look at the matter this way: you should only be writing code that is (a) correct (otherwise your program is plain wrong) and (b) necessary (otherwise your code is too large, which means more RAM necessary, more cycles spent on useless things, more effort to understand it, more time spent on maintaining it etc. etc.)

Now consider what it is that you want done in a finalizer. Either it's necessary. In that case you can't put it into a finalizer, because you don't know if it will be called. That's not good enough. Or it it not necessary - then you shouldn't be writing it in the first place! Either way, putting it into the finalizer is the wrong choice.

(Note that the examples you name, like closing file streams, look as if they're not really necessary, but they are. It's just that until you hit the limit for open file handles on your system, you won't notice that your code is incorrect. But that limit is a feature of the operating system and is therefore even more unpredictable than the JVM's policy for finalizers, so it really, really is important that you don't waste file handles.)

2
  • If I'm only supposed to write code that is "necessary" then should I avoid all of the styling in all my GUI applications? Surely none of it is necessary; the GUIs will work just fine without styling, they'll just look horrid. About the finalizer, it can be necessary to do something, but still ok to put it in a finalizer, since the finalizer will be called during object GC, if at all. If you need to close a resource, specifically when it's ready for garbage collection, then a finalizer is optimal. Either the program terminates releasing your resource or the finalizer is called.
    – Kröw
    Jun 20, 2019 at 23:35
  • If I have a RAM heavy, expensive-to-create, closeable object and I decide to reference it weakly, so that it can be cleared when not needed, then I could use finalize() to close it in case a gc cycle really needs to free up RAM. Otherwise, I'd keep the object in RAM instead of having to regenerate and close it every time I need to use it. Sure, resources it opens won't be freed until the object is GCed, whenever that may be, but I may not need to guarantee that my resources be freed at a certain time.
    – Kröw
    Jun 20, 2019 at 23:35
9

In addition to why leaving it to the finalizer to release resources is generally a bad idea, finalizeable objects come with performance overhead.

From Java theory and practice: Garbage collection and performance (Brian Goetz), Finalizers are not your friend:

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.

1
  • Excellent point. See my answer which provides a means of avoiding the performance overhead of finalize().
    – Mike Nakis
    Jul 5, 2015 at 10:13
8

One of the biggest reasons not to rely of finalizers is that most of the resources one might be tempted to clean up in the finalizer are very limited. The garbage collector only runs every so often, since traversing references to determine whether or not something can be released is expensive. This means that it could 'be a while' before your objects actually get destroyed. If you have a lot of objects opening up short-lived database connections, for example, leaving the finalizers to clean up these connections could exhaust your connection pool while it waits for the garbage collector to finally run and free up finished connections. Then, because of the wait, you wind up with a large backlog of queued up requests, which quickly exhaust the connection pool again. It's a cycle that will quickly put your application into a crippled state of operation.

Additionally, the use of try-with-resources makes closing 'closable' objects on completion easy to do. If you are not familiar with this construct, I suggest you check it out: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

7

My (least) favourite reason for avoiding Object.finalize is not that objects might get finalized after you expect it, but they can get finalized before you might expect it. The problem is not that an object that is still in scope can be finalized before the scope is exited if Java decides it is no-longer reachable.

void test() {
   HasFinalize myObject = ...;
   OutputStream os = myObject.stream;

   // myObject is no-longer reachable at this point, 
   // even though it is in scope. But objects are finalized
   // based on reachability.
   // And since finalization is on another thread, it 
   // could happen before or in the middle of the write .. 
   // closing the stream and causing much fun.
   os.write("Hello World");
}

See this question for more details. Even more fun is that this decision might only be made after hot-spot optimisation kicks in, making this painful to debug.

2
  • 1
    The thing is that HasFinalize.stream should itself be a separately finalizable object. That is, the finalization of HasFinalize shouldn't finalize or try to clean-up stream. Or if it should, then it should make stream inaccessible.
    – acelent
    Jul 6, 2015 at 9:18
  • 1
    It’s even worse
    – Holger
    Jul 6, 2015 at 12:50
4

I have to be constantly checking if an object has an implementation of close(), and I'm sure I have missed a few calls to it at some points in the past.

In Eclipse, I get a warning whenever I forget to close something that implements Closeable/AutoCloseable. I'm not sure if that's an Eclipse thing or if it's part of the official compiler, but you might look into using similar static analysis tools to help you there. FindBugs, for example, can probably help you check whether you've forgotten to close a resource.

1
  • 1
    Good idea mentioning AutoCloseable. It makes it brain-dead-easy to manage resources with try-with-resources. This nullifies several of the arguments in the question.
    – user22815
    Jul 4, 2015 at 21:47
2

To your first question:

It is up to the VM implementation and the GC to determine when the right time to deallocate an object is, not the developer. Why is it important to decide when Object.finalize() gets called?

Well, the JVM will determine, when it is a good point to reclaim the storage that has been allocated for an object. This is not necessarily the time when the resource clean up, you want to perform in finalize(), should happen. This is illustrated in the “finalize() called on strongly reachable object in Java 8” question on SO. There, a close() method has been called by a finalize() method, while an attempt to read from the stream by the same object is still pending. So besides the well-known possibility that finalize() gets called way to late there is the possibility that it gets called too early.

The premise of your second question:

Normally, and correct me if I'm wrong, the only time Object.finalize() wouldn't get called is when the application got terminated before the GC got a chance to run.

is simply wrong. There is no requirement for a JVM to support finalization at all. Well, it’s not completely wrong as you could still interpret it as “the application got terminated before finalization took place”, assuming that your application will ever terminate.

But note the small difference between “GC” of your original statement and the term “finalization”. Garbage collection is distinct to finalization. Once the memory management detects that an object is unreachable, it may simply reclaim its space, if either, it doesn’t have a special finalize() method or finalization is simply unsupported, or it may enqueue the object for finalization. Thus, the completion of a garbage collection cycle does not imply that finalizers are executed. That may happen at a later time, when the queue is processed, or never at all.

This point is also the reason why even on JVMs with finalization support, relying on it for resource cleanup is dangerous. Garbage collection is part of the memory management and hence triggered by memory needs. It’s possible that garbage collection never runs because there is enough memory during the whole runtime (well, that still fits into the “the application got terminated before the GC got a chance to run” description, somehow). It’s also possible that the GC does run but afterwards, there is enough memory reclaimed, so the finalizer queue is not processed.

In other words, native resources managed this way are still an alien to the memory management. While it is guaranteed that an OutOfMemoryError is only thrown after sufficient attempts to free memory, that does not apply to native resources and finalization. It is possible that opening a file fails due to insufficient resources while the finalization queue is full of objects which could free these resources, if the finalizer ever ran…

Not the answer you're looking for? Browse other questions tagged or ask your own question.