Why is the finalize() method deprecated in Java 9?
Asked Answered
G

1

32

(This question is different from Why would you ever implement finalize()? This question is about deprecation from the Java platform, and the other question is about whether one should use this mechanism in applications.)

Why is the finalize() method deprecated in Java 9?

Yes it could be used in wrong way (like save an object from garbage collecting [only one time though] or try to close some native resources within it [it's better than don't close at all though]) as well as many other methods could be used wrongly.

So is finalize() really so dangerous or absolutely useless that it's necessary to kick it out of Java?

Gelding answered 14/5, 2019 at 23:8 Comment(15)
See also the discussions linked from the OpenJDK bug: bugs.openjdk.java.net/browse/JDK-8165641Frier
Short answer, though: Yes. It is useless far more often than people think, dangerous to use even for experts who understand it very well (for example, unless you know what a reachability fence is and how to use one, I guarantee that your finalizers that manage native resources are all buggy), and it's an attractive nuisance for people who are looking for C++-style destructors. I say: farewell to finalizers, and good riddance.Frier
Your question may be different, but the answer is the same. (That's why the message is phrased as "This question already has an answer here.") If there is no valid use case, then deprecation is natural.Frier
Here is a thread about what you should replace it with: https://mcmap.net/q/68012/-replacing-finalize-in-javaBifoliate
While @DanielPryden has already explained why it doesn't really matter that your question is different from the duplicate, I'd also add that your question is probably off topic for Stack Overflow since it is not really about a programming issue, and any answers would be "primarily opinion-based". (I suppose you could have been incredibly lucky and received an answer from the person who was responsible for deciding to deprecate finalize() in JDK 10 but it's pretty unlikely, and any answer from anyone else would have been mere speculation.)Parted
@Parted Too bad this question was closed. There is a set of questions about the rationale for decisions that certainly can be answered. Either people involved in the decision might show up and answer it -- which has certainly happened before, particularly in regard to recent decisions -- or the answer might be written down somewhere, either in a mailing list post or a comment in a bug report. Rationale may be subjective, but it can be definitive and not purely opinion-based.Beisel
@DanielPryden You don't know that the answer is the same, unless you were involved in the decision. I was; I don't think you were.Beisel
@StuartMarks: You are of course correct. The only knowledge I have of the rationale is what is publicly available in the mailing list archives. If you feel that the question can be meaningfully answered without just devolving into a re-hash of why finalizers are problematic, I have no objection. I will refrain from any further actions on this question; please vote to re-open if you wish. I will admit that part of my eagerness to dupe-close the question is the somewhat rant-y nature of the phrasing ("So is finalize() method really so dangerous or absolutely useless to kick it out from Java?").Frier
@StuartMarks: For what it's worth, since you hold a gold badge in the java tag, you can single-handedly reopen the question if you believe it should be open.Frier
@DanielPryden Thanks for your responses. The un-dupe-hammer works! I didn't know that. I knew about the dupe-hammer but I seem to recall having to vote and wait for other votes to reopen closed questions in the past. In any case I'll provide an answer at some point.Beisel
@StuartMarks Well I certainly take your (implied) point that since you were involved in the decision to deprecate Object.finalize(), it may make sense to reopen this, as suggested in another comment. I guess Object.finalize() may have been deprecated specifically in Java 9 (as opposed to any earlier/later release) because of the the addition of the Cleaner class in Java 9, and JDK-8165641 Deprecate Object.finalize somewhat supports that view, but that's just pure speculation on my part. Anyway, voting to reopen based on your comments.Parted
@StuartMarks Heh - I see the question was reopened while I was writing my comment.Parted
@Parted Yep, Daniel Pryden pointed out to me that I could do it myself, so I did! In any case, thanks for your response. I think there is more to be said that's not in the other answer. Sorry I was a bit snippy about this -- I do think that questions about rationale are different from pure "opinion-based" questions that are things like "Is X better than Y?" and that it's possible to have useful (though subjective) answers about rationale.Beisel
@StuartMarks [1] I agree with all that, and with hindsight I was obviously hasty in suggesting that it was unlikely for anyone involved in the decision to deprecate finalize() to respond here - my bad. [2] Though not a duplicate, Should Java 9 Cleaner be preferred to finalization? seems relevant.Parted
@Parted (Ah, it's closed-opinion-based where I've had to wait for reopen votes, whereas this one was reopened immediately because it was closed as a duplicate. I had mixed those up.) In any case maybe I reopened too quickly, because this question now seems to be attracting poor answers!Beisel
B
49

Although the question was asking about the Object.finalize method, the subject really is about the finalization mechanism as a whole. This mechanism includes not only the surface API Object.finalize, but it also includes specifications of the programming language about the life cycle of objects, and practical impact on garbage collector implementations in JVMs.

Much has been written about why finalization is difficult to use from the application's point of view. See the questions Why would you ever implement finalize()? and Should Java 9 Cleaner be preferred to finalization? and their answers. See also Effective Java, 3rd edition by Joshua Bloch, Item 8.

Briefly, some points about the problems associated with using finalizers are:

  • they are notoriously difficult to program correctly

  • in particular, they can be run unexpectedly when an object becomes unreachable unexpectedly (but correctly); for example, see my answer to this question

  • finalization can easily break subclass/superclass relationships

  • there is no ordering among finalizers

  • a given object's finalize method is invoked at most once by the JVM, even if that object is "resurrected"

  • there are no guarantees about timeliness of finalization or even that it will run at all

  • there is no explicit registration or deregistration mechanism

The above are difficulties with the use of finalization. Anyone who is considering using finalization should reconsider, given the above list of issues. But are these issues sufficient to deprecate finalization in the Java platform? There are several additional reasons explained in the sections below.

Finalization Potentially Makes Systems Fragile

Even if you write an object that uses finalization correctly, it can cause problems when your object is integrated into a larger system. Even if you don't use finalization at all, being integrated into a larger system, some parts of which use finalization, can result in problems. The general issue is that worker threads that create garbage need to be in balance with the garbage collector. If the garbage collector falls behind, at least some collectors can "stop the world" and do a full collection to catch up. Finalization complicates this interaction. Even if the garbage collector is keeping up with application threads, finalization can introduce bottlenecks and slow down the system, or it can cause delays in freeing resources that result in exhaustion of those resources. This is a systems problem. Even if the actual code that uses finalization is correct, problems can still occur in correctly programmed systems.

(Edit 2021-09-16: this question describes a problem where a system works fine under low load but fails under high load, likely because the relative rate of allocation outstrips the rate of finalization under high load.)

Finalization Contributes to Security Issues

The SEI CERT Oracle Coding Standard for Java has a rule MET12-J: Do not use finalizers. (Note, this is a site about secure coding.) In particular, it says

Improper use of finalizers can result in resurrection of garbage-collection-ready objects and result in denial-of-service vulnerabilities.

Oracle's Secure Coding Guidelines for Java SE is more explicit about potential security issues that can arise using finalization. In this case it is not a problem with code that uses finalization. Instead, finalization can be used by an attacker to attack sensitive code that hasn't properly defended itself. In particular, Guideline 7-3 / OBJECT-3 states in part,

Partially initialized instances of a non-final class can be accessed via a finalizer attack. The attacker overrides the protected finalize method in a subclass and attempts to create a new instance of that subclass. This attempt fails ... but the attacker simply ignores any exception and waits for the virtual machine to perform finalization on the partially initialized object. When that occurs the malicious finalize method implementation is invoked, giving the attacker access to this, a reference to the object being finalized. Although the object is only partially initialized, the attacker can still invoke methods on it....

Thus, the presence of the finalization mechanism in the platform imposes a burden on programmers who are trying to write high assurance code.

Finalization Adds Complexity to Specifications

The Java Platform is defined by several specifications, including specifications for the language, the virtual machine, and the class library APIs. Impact of finalization is spread thinly across all of these, but it repeatedly makes its presence felt. For example, finalization has a very subtle interaction with object creation (which is already complicated enough). Finalization also has appeared Java's public APIs, meaning that evolution of those APIs has (up to now) been required to remain compatible with previously specified behaviors. Evolving these specifications is made more costly the presence of finalization.

Finalization Adds Complexity to Implementations

This is mainly about garbage collectors. There are several garbage collection implementations, and all are required to pay the cost of implementing finalization. The implementations are quite good at minimizing the runtime overhead if finalization isn't used. However, the implementation still needs to be there, and it needs to be correct and well tested. This is an ongoing development and maintenance burden.

Summary

We've seen elsewhere that it's not recommended for programmers to use finalization. However, if something is not useful, it doesn't necessarily follow that it should be deprecated. The points above illustrate the fact that even if finalization isn't used, the mere presence of the mechanism in the platform imposes ongoing specification, development, and maintenance costs. Given the lack of usefulness of the mechanism and the costs it imposes, it makes sense to deprecate it. Eventually, getting rid of finalization will benefit everyone.

As of this writing (2019-06-04) there is no concrete plan to remove finalization from Java. However, it is certainly the intent to do so. We've deprecated the Object.finalize method, but have not marked it for removal. This is a formal recommendation that programmers stop using this mechanism. It's been known informally that finalization shouldn't be used, but of course it's necessary to take a formal step. In addition, certain finalize methods in library classes (for example, ZipFile.finalize) have been deprecated "for removal" which means that the finalization behavior of these classes may be removed from a future release. Eventually, we hope to disable finalization in the JVM (perhaps first optionally, and then later by default), and at some point in the future actually remove the finalization implementation from garbage collectors.

(Edit 2021-11-03: JEP 421 has just been posted, which proposes to deprecate finalization for removal. At this writing it's in the "candidate" state but I expect it will move forward. The deprecations added by this JEP are a formal notification that finalization will be removed at some point in a subsequent Java release. Perhaps not surprisingly, there's a fair overlap between the material in this answer and in the JEP, though the JEP is more precise and describes a moderate evolution in our thinking on the topic.)

(Edit 2022-04-04: JEP 421 Deprecate Finalization for Removal has been integrated and delivered in JDK 18.)

Beisel answered 5/6, 2019 at 4:41 Comment(15)
Finalization really is a complex matter. So there’s a small correction: only a finalizer trying to resurrect itself works only once. But a finalizer may resurrect a different encapsulated finalizer-reachable object, followed by creating a new instance of that wrapper which can resurrect the object again, a procedure that can be done an arbitrary number of times. Not that this was a recommended pattern…Communicative
@Communicative True. The rule is about invocation of the finalize method, not about "resurrection" per se. I've updated the text to be more precise.Beisel
@StuartMarks finalization can easily break subclass/superclass relationships How?Mousse
@GovindaSakhare Briefly, if a subclass fails to call super.finalize() for any reason, finalization of the superclass wont occur.Beisel
If the finalization mechanism is the problem, why isn't usingPhantomReferences/Cleaner just as dangerous? And if they aren't dangerous, why not just reimplement finalize using PhantomReferences/Cleaner internally?Xenocryst
@Xenocryst The thing that most makes finalizers dangerous is the ability to “resurrect” an object during finalization. That cannot happen with Cleaner or Reference processing. There are other less serious issues too but that’s the biggest. See JEP 421 for a full discussion.Beisel
I've never really bought the argument that the ability to resurrect an object in finalize means finalize is broken. Don't resurrect it! There are many other things in Java which are dangerous if you fail to follow guidelines. Any hashCode() or equals() implementation which references a non-final field is hugely dangerous; any equals() implementation which uses different fields to the hashCode() implementation is hugely dangerous; but I don't hear anyone clamouring to deprecate hashCode() or equals(). However I accept that your explanation probably reflects Oracle's reasoning.Xenocryst
@Xenocryst Following the guidelines is insufficient. Reread the section above where it says, "Finalization can be used by an attacker to attack sensitive code that hasn't properly defended itself."Beisel
So far as I can tell from that description, to fix it the JVM simply shouldn't call finalize on partially constructed objects - or at least, certainly not on ones which fail to complete construction due to security exceptions. Doesn't seem like a deal-breaker. I guess I sound like a dog with a bone... I just think fixing things beats deprecating them, where they can be fixed.Xenocryst
@Xenocryst Well what you say sounds simple but in fact is not. The JVM would need to enable finalization only upon return from the very first constructor call, while in general, object construction involves many nested constructor calls. It is probably possible to fix this with some effort, but given that finalization is the wrong programming model to begin with, we decided to get rid of it instead.Beisel
I assume you mean it is the wrong model because freeing non-memory resources only in response to GC events is wrong-headed. Which I accept, but it leaves me wondering why Cleaner was introduced, as it seems equally to be based on the wrong programming model. Try-with-resources is the correct programming model most of the time, but is no use if the resource needs to outlive its declared scope. So since neither ref counting or Rust-style statically checked "handing off" of clean-up responsibility is available, I don't think there is a right programming model (language/library supported) here?Xenocryst
@Xenocryst you’re right that cleaner shouldn’t be the preferred way to resource cleanup either. However, it does solve lots of the issues of finalization. Not only does the explicit opt in (and even opt out) ensure that there can’t be a clean up attempt on an insufficiently constructed object, it’s also impossible to override by subclasses while the subclasses still can register their own cleaner. I don’t see, how this could ever be fixed for the finalize() method. You can only allow overriding or forbid overriding. You can not have a non-overridable action and support subclass finalizationCommunicative
Did you consider moving to C#/.NET approach for finalization? It addresses several of the flaws pointed out against Java finalization strategy, and would have preserved ability to access the instance during the destruction. With the Cleaner strategy you are now requiring to add another level of indirection to cleanup resources that are used during the entire object lifetime: it doesn't look like a big win for me, performance-wise.Indoxyl
@Indoxyl No, we didn't. Finalization in C#/.NET seems broadly similar to Java finalization and shares several of its fundamental flaws: it can "resurrect" the unreachable object; it requires another GC pass to reclaim the object after finalization; finalization is not guaranteed and may occur after an arbitrary delay; there is no control over ordering of finalizers; a blocking finalizer can prevent other finalizers from running; in addition to other problems. See Dispose Pattern for further discussion.Beisel
You are right that some of the flaws you are objecting are probably still there in C#/.NET, but they found a balance that may allow them not deprecate a fundamental aspect of the language so late. I still observe that to cost of Cleaner strategy (no access to object instance) is honestly too big and inconvenient to me: you could have implemented Enabled on-demand/No interference as mentioned in JEP 421 with C#/.NET style destructors and you could have prevented object resurrection with language static analysis instead.Indoxyl

© 2022 - 2024 — McMap. All rights reserved.