44

According to this post, we should never rely on the finalize method to be called. So why did Java include it in the programming language at all?

It seems like a terrible decision to include in any programming language a function that might get called.

patstuart
  • 643
  • 5
  • 12

7 Answers7

56

Finalizers are important for the management of native resources. For example, your object might need to allocate a WidgetHandle from the operating system using a non-Java API. If you don't release that WidgetHandle when your object is GC'd, you're going to be leaking WidgetHandles.

What's important is that the "finalizer is never called" cases break down rather simply:

  1. The program shuts down quickly
  2. The object "lives forever" during the lifetime of the program
  3. The computer turns off / your process is killed by the OS / etc

In all three of these cases, you either don't have a native leak (by virtue of the fact that your program is not running anymore), or you already have a non-native leak (if you keep allocating managed objects without them being GC'd).

The "don't rely on the finalizer being called" warning is really about not using finalizers for program logic. For example, you don't want to keep track of how many of your objects exist across all instances of your program by incrementing a counter in a file somewhere during construction and decrementing it in a finalizer -- because there's no guarantee that your objects will be finalized, this file counter will probably not ever go back to 0. This is really a special case of the more general principle that you shouldn't depend on your program terminating normally (power failures, etc).

For management of native resources, though, the cases where the finalizer doesn't run correspond to cases where you don't care if it doesn't run.

Ryan Cavanaugh
  • 779
  • 4
  • 8
  • Excellent answer. I was like even if it might be called.. it should still be included. I was confused for a while with the question and the linked one on StackOverflow. – InformedA Jun 25 '14 at 18:35
  • 3
    It wasn't in the question, but it's worth discussing in the context of "don't rely on the finalizer being called": The finalizer may be called, but only after an arbitrarily long delay after the object becomes unreachable. This matters for "native resources" which are far more scarce than memory (which are most of them, it turns out). –  Jun 25 '14 at 18:50
  • 26
    "Finalizers are important for the management of native resources." - nooooooooo, ahem sorry. Using finalizers for managing native resources is a horrible idea (which is why we have autocloseable and co now). The GC only cares about memory, not any other resource. This means you can run out of native resources but still have enough memory to not trigger a GC - we really don't want that. Also finalizers make GC more expensive which isn't a good idea either. – Voo Jun 25 '14 at 21:42
  • 12
    I agree you should still have an explicit native resource management strategy other than finalizers, but if your object *does* allocate them and doesn't free them in the finalizer, you're opening yourself up to a native leak in the case where the object is GC'd without the explicit freeing of the resource. In other words, finalizers are an important *part* of a native resource management strategy, not a solution to the problem in general. – Ryan Cavanaugh Jun 25 '14 at 22:21
  • The "don't rely on" part probably stems from the very high possibility that by the time the finalizer is invoked, the object (and its Java aka non-native fields) are no longer in a "valid object state" in the usual Java sense. That is, there may be a few dangling references to native resources, and then some other Java fields had become non-dereferenceable. Same situation in C# finalizer. – rwong Jun 26 '14 at 06:55
  • 1
    @Voo it's probably more correct to say the finalizer is the fallback in case the contract of the object is not followed (`close()` is never called before it becomes unreachable) – ratchet freak Jun 26 '14 at 10:34
  • What about the case where the resource may be needed (either by the program that originally acquired it, or by another program) before the GC has run the `finalize` method upon the abandoned object encapsulating it. – supercat Jul 02 '14 at 16:28
41

According to Joshua Bloch's Effective Java (Second Edition), there are two scenarios when finalize() is useful:

  1. One is to act as a “safety net” in case the owner of an object forgets to call its explicit termination method. While there’s no guarantee that the finalizer will be invoked promptly, it may be better to free the resource late than never, in those (hopefully rare) cases when the client fails to call the explicit termination method. But the finalizer should log a warning if it finds that the resource has not been terminated

  2. A second legitimate use of finalizers concerns objects with native peers. A native peer is a native object to which a normal object delegates via native methods. Because a native peer is not a normal object, the garbage collector doesn’t know about it and can’t reclaim it when its Java peer is reclaimed. A finalizer is an appropriate vehicle for performing this task, assuming the native peer holds no critical resources. If the native peer holds resources that must be terminated promptly, the class should have an explicit termination method, as described above. The termination method should do whatever is required to free the critical resource.

For further reading, refer to Item 7, page 27.

bbalchev
  • 584
  • 5
  • 11
  • 4
    With #2 sounds like the native peer is a native resource that requires a safety net and should have a termination method, and thus shouldn't be a separate point. – Mooing Duck Jun 26 '14 at 00:05
  • 2
    @MooingDuck: #2 is a separate point because, if the native peer does not hold critical resources (e.g. it is purely an in-memory object), then there is no need to expose an explicit termination method. The safety net from #1 is explicitly about having a termination method. – jhominal Jun 26 '14 at 05:09
5

Purpose of this method is explained in API documentation as follows:

it is invoked if and when the Java virtual machine has determined that there is no longer any means by which this object can be accessed by any thread that has not yet died, except as a result of an action taken by the finalization of some other object or class which is ready to be finalized...

the usual purpose of finalize... is to perform cleanup actions before the object is irrevocably discarded. For example, the finalize method for an object that represents an input/output connection might perform explicit I/O transactions to break the connection before the object is permanently discarded...


If you're additionally interested in reasons why language designers have chosen that "object is irrevocably discarded" (garbage collected) the way that is beyond application programmer control ("we should never rely"), this has been explained in an answer to related question:

automatic garbage collection... eliminates entire classes of programming errors that bedevil C and C++ programmers. You can develop Java code with confidence that the system will find many errors quickly and that major problems won't lay dormant until after your production code has shipped..

Above quote, in turn, was taken from official documentation about Java design goals, that is it can be considered authoritative reference explaining why Java language designers decided this way.

For a more detailed and language agnostic discussion of this preference, refer OOSC section 9.6 Automatic memory management (actually, not only this section but the whole chapter 9 is very worth reading if you're interested in stuff like that). This section opens with an unambiguous statement:

A good O-O environment should offer an automatic memory management mechanism which will detect and reclaim unreachable objects, allowing application developers to concentrate on their job — application development.

The preceding discussion should suffice to show how important it is to have such a facility available. In the words of Michael Schweitzer and Lambert Strether:

An object-oriented program without automatic memory management is roughly the same as a pressure cooker without a safety valve: sooner or later the thing is sure to blow up!

gnat
  • 21,442
  • 29
  • 112
  • 288
4

Finalizers exist because they were expected to be an effective means of ensuring that things get cleaned up (even though in practice they aren't), and because when they were invented, better means of ensuring cleanup (such as phantom references and try-with-resources) didn't exist yet. In hindsight, Java would likely be better if the effort spent on implementing its "finalize" facility had been spent on other means of cleanup, but that was hardly clear during the time Java was initially being developed.

supercat
  • 8,335
  • 22
  • 28
3

CAVEAT: I may be out of date, but this is my understanding as of a few years ago:

In general, there is no guarantee of when a finalizer runs -- or even that it runs at all, though some JVMs will let you request a complete GC and finalization before the program exits (which, of course, means that the program takes longer to exit, and which isn't the default mode of operation).

And some GCs were known to explicitly delay or avoid GC'ing objects that had finalizers, in the hope that this would produce better performance on benchmarks.

These behaviors unfortunately conflict with the original reasons finalizers were recommended, and have encouraged the use of explicitly-called shutdown methods instead.

If you have an object that really has to be cleaned up before being discarded, and if you really can't trust the users to do so, a finalizer may still be worth considering. But in general, there are Good Reasons that you don't see them as often in modern Java code as you did in some of the early examples.

keshlam
  • 223
  • 1
  • 5
1

The Google Java Style Guide has some sage advice on the subject:

It is extremely rare to override Object.finalize.

Tip: Don't do it. If you absolutely must, first read and understand Effective Java Item 7, "Avoid Finalizers," very carefully, and then don't do it.

Daniel Pryden
  • 3,268
  • 1
  • 21
  • 21
  • 5
    Why I admire the oversimplification of the problem, "don't do it" is not an answer to "why does it exist". – Pierre Arlaud Jun 26 '14 at 08:41
  • 1
    I guess I didn't spell it out well in my answer, but (IMO) the answer to "why does it exist?" is "it shouldn't". For those rare cases where you really need a finalization operation, you probably want to build it yourself with `PhantomReference` and `ReferenceQueue` instead. – Daniel Pryden Jun 26 '14 at 14:54
  • I meant while* obviously, though I didn't downvote you. The most upvoted answer does however show how useful the method is, kinda invalidating your point. – Pierre Arlaud Jun 26 '14 at 14:57
  • 1
    Even in the native peer case, I think `PhantomReference` is a better solution. Finalizers are a wart left over from the early days of Java, and like `Object.clone()` and raw types, are a part of the language best forgotten. – Daniel Pryden Jun 26 '14 at 15:05
  • The quoted Style Guide might work in a non-JNI environment or when Java processes can be killed and respawned with ease (in the case of distributed high-redundancy high-availability systems, as is Google). A more nuanced guide is, whenever one wants to use `finalize`, (1) provide an explicit cleanup method such as `close()` or `destroy()`, and then (2) realize that `finalize` is ineffective for cleanup work that involves Java calls (as other dependent objects may have already been destroyed, resulted in invalid state), and (3) only implement it if not doing so could cause a failure elsewhere. – rwong Jul 02 '14 at 13:31
  • The documentation for `PhantomReference` is perhaps too cryptic for most Java developers. It enables a program to perform occasional **post-mortem actions, without contacting the dead**. In some cases, this involves wrapping the necessary secondary resources that are needed to initiate a cleanup. Please see [this article](https://weblogs.java.net/blog/kcpeppe/archive/2011/09/29/mysterious-phantom-reference) for a better understanding. – rwong Jul 02 '14 at 13:46
  • @rwong: Indeed. There are very few environments where (a) native leaks are a serious problem, (b) there is insufficient redundancy to tolerate JVM restarts, (c) there is no practical way to structure or audit the codebase to ensure that `close()` methods or similar are actually called, and (d) a sometime-later best-effort cleanup process is actually sufficient. In a case where all of those are true, then finalizers are perhaps a good solution. However, I think the advice to Read *Effective Java* first is sound: unless you fully understand finalizers, you're probably not competent to use them. – Daniel Pryden Jul 02 '14 at 17:54
  • @DanielPryden: Thanks. However, native leaks are a serious problem for the majority of Desktop Java applications, because they must interact with the OS underlying the framework code base which is based on JNI. – rwong Jul 02 '14 at 19:09
  • 1
    [The dangers of finalizers are described in a more accessible (understandable by developers) manner in C#.](http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx), Though, one **must not** imply that the underlying mechanisms on Java and C# are same; there's nothing in their specifications that say so. – rwong Jul 02 '14 at 19:12
1

The Java Language Specification (Java SE 7) states:

Finalizers provide a chance to free up resources that cannot be freed automatically by an automatic storage manager. In such situations, simply reclaiming the memory used by an object would not guarantee that the resources it held would be reclaimed.

Benni
  • 896
  • 2
  • 7
  • 15