6

Quotes from SCJP 6 study guide:

In the finalize() method you could write code that passes a reference to the object in question back to another object, effectively uneligiblizing the object for garbage collection. If at some point later on this same object becomes eligible for garbage collection again, the garbage collector can still process this object and delete it. The garbage collector, however, will remember that, for this object, finalize() already ran, and it will not run finalize() again

Why is it designed so? The purpose of the finalize() method still holds good even when the object is marked of collection second time. Then why Java decides to skip call to finalize()?

Rahul Kurup
  • 693
  • 3
  • 9
  • 22
  • 3
    Where is that quote from? – Jashaszun Aug 04 '15 at 17:31
  • 7
    Because the garbage collection assumes that finalize() should not make the object uneligible to GC. If it does, it's a bug, and you should fix it. Anyway, that's interesting and everything, but in 18 years programming in Java, I don't think I've ever used finalize(). Correct code shouldn't need it. – JB Nizet Aug 04 '15 at 17:33
  • 2
    @JBNizet I was thinking the same thing, but [the Javadocs](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#finalize--) don't specify that contract, instead mentioning that "The finalize method may take any action, including making this object available again to other threads" (in the context that this isn't the usual use of `finalize`, but that it's still an option). – FThompson Aug 04 '15 at 17:36
  • @Jashaszun; Added the quote source. – Rahul Kurup Aug 04 '15 at 17:36
  • @JBNizet: It is not specified in the Javadocs that we shouldn't be doing anything that make the object un-eligible for garbage collection. Is there any GC design related reason behind this particular behavior ? – Rahul Kurup Aug 04 '15 at 17:42
  • 3
    @Vulcan Hmm, interesting. I'd say then that if you make the object uneligible, it's *probably* a bug, and if it's not, then you're on your own and should be smart enough to make a second call to finalize() unnecessary. My main point is: in 99.9999% of the cases, finalize() is useless. In 99.9999% of the cases where you use it, your shouldn't make the object uneligible to GC in it. – JB Nizet Aug 04 '15 at 17:42
  • Why is it designed so? That same question occurred to me once too, but since `finalize()` is such a fundamentally Bad Idea, I never really cared to find out why it also has warts. – Solomon Slow Aug 04 '15 at 17:59
  • 1
    @JBNizet You might be interested in the term "object resurrection". – chrylis -cautiouslyoptimistic- Aug 04 '15 at 22:27

2 Answers2

4

I don't know if its the original reason, but the current implementation enqueues Finalizer instances (internal subclass of Reference) for objects overriding the finalize method with an internal ReferenceQueue that gets polled by a dedicated FinalizerThread.

And because the JVM has no way of knowing whether the object would need to be finalized a second time it cannot decide whether it would have to enqueue a new Finalizer once the finalize() method has been called.

Anyway, you should avoid using finalize(). It makes object allocation more costly, prevents escape analysis and is not a very reliable way of managing native resources because the GC can postpone the finalization for an unbounded amount of time.

the8472
  • 40,999
  • 5
  • 70
  • 122
  • Seconded. Generally 'rescuing' an object from the GC means putting it somewhere for it to be used again. If that 'someplace' has to be thread safe, and in Java nearly everything has to be thread safe, then the costs of locking to add or remove objects from that list can become comparable to the cost of object allocation to begin with. If you insist on messing around in this space, which for anything other than educational reasons is highly ill advised, at least do a proper benchmark with threading involved, not a microbenchmark that will lie. – Jason Aug 06 '15 at 00:03
2

Objects with an enabled finalizer are ineligible for collection; the GC only examines them after determining all the other objects which are ineligible for collection, however, and makes note of all the objects which would have been eligible for collection but for the existence of the enabled finalizer, and runs the finalize methods of such objects as soon as practical. Finalizable objects won't become eligible for collection until the finalizer has run, but the GC will have no way of distinguishing objects which become eligible for finalization as soon as the finalizer finishes, or those which were rendered ineligible for finalization as a result of actions by some object's finalizer and became eligible for collection at some later time.

The .NET Framework includes methods called IIRC GC.SuppressFinalize and GC.ReRegisterForFinalization which make it possible for code which knows an object's finalizer won't do anything useful to tell the GC not to bother calling it, and allows for code which knows a finalizer ran "too soon" to request that it run again later. The JVM, however, does not include such a feature. Since having all finalizable objects automatically reregistered for finalization once the finalizer runs would prevent them from ever getting collected, and since there's no way to manually reregister them, the net consequence is that there's no usable pattern via which an object's finalizer can be run more than once.

On the other hand, it is possible to achieve a similar effect by defining a nested class object which is finalizable, having the outer class object hold a reference to a nested-class instance, and having that nested class instance's "finalize" method chain back to cleanup code in its owner. If that cleanup code discards the nested-class instance and replaces it with a new one, then that new instance will trigger its finalizer (chaining back to its owner) on the next GC cycle where the owner is found to be unreferenced.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • Since the outer and inner object both refer to each other, it isn’t an outer-inner relationship actually, but just a cycle. I remember having seen descriptions naming it the other way round, a finalizable outer object reclaiming an encapsulated inner object by wrapping in into a new outer object… – Holger Jul 26 '17 at 12:07
  • @Holger: I was suggesting nesting the class definitions, which would establish a definite inner/outer relationship between the *classes*. The *instances* would hold circular references (with the inner->outer reference being implicit) but the type definitions would be nested. – supercat Jul 26 '17 at 14:41