Quality Outreach Heads-up - JDK 27: Removal of ThreadPoolExecutor.finalize()

The OpenJDK Quality Group is promoting the testing of FOSS projects with OpenJDK builds as a way to improve the overall quality of the release. This heads-up is part of a regular communication sent to the projects involved. To learn more about the program, and how-to join, please check here.

Deprecation and Removal of Finalizers

Before the introduction of try-with-resources and the Cleaner API, classes that needed to release resources before their end of life would implement Object.finalize(). The garbage collector invokes this method after an object becomes unreachable and before reclaiming its memory. This process, known as finalization, has a difficult programming model that is prone to unreliable implementation, can cause security vulnerabilities, and negatively impacts performance. It has thus been deprecated for removal in favor of the aforementioned alternatives in JDK 18, as described in JDK Enhancement Proposal 421.

Since then, the number of finalize implementations in the JDK has been steadily reduced. Likewise, the Java ecosystem is required to search for and remove finalize implementations to prepare for the eventual removal of this mechanism. See JEP 421 for details how to approach this.

Removal of ThreadPoolExecutor.finalize()

ThreadPoolExecutor is a non-final implementation of ExecutorService that may be a superclass to executor services in frameworks, libraries, and applications. Its finalize-method has been…

JDK 27 will remove the empty method, which can lead to compile errors in existing code.

Source-Incompatible Change

While Object.finalize() declares that it throws Throwable, ThreadPoolExecutor.finalize() does not. That means calls to ThreadPoolExecutor.finalize(), either directly or by an extending class through super.finalize(), will now implicitly call Object.finalize() and thus encounter a Throwable when they didn’t before, which is likely to result in a compile error.

Projects encountering this situation are strongly encouraged to remove this (and other) uses of finalize().

If absolutely necessary, a workaround would be to encase the invocation of finalize() in a try block. Since this ends up calling Object.finalize(), which is empty, the catch block can likewise be empty.

~