Replacing finalize() in Java
Asked Answered
D

4

31

Object.finalize() is deprecated in Java 9, and I think I understand the reasons why, but I'm having trouble seeing how to replace it.

I have a utility class called Configuration which essentially has a single instance that owns everything in the application and lasts for the duration of the application. One of the services it provides is logging: on first request to log a message, a logger is created (for various legacy reasons it's my own Logger rather than a standard one), with a reference held in a field of the Configuration object, and on application termination, whether normal or abnormal, I want to release any resources held by the logger (which is a black box since users of my library can supply their own implementation).

Currently this is achieved with a Configuration.finalize() method that calls logger.close().

What should I be doing instead?

Dentilingual answered 11/12, 2017 at 23:16 Comment(2)
FYIEulau
Since finalization is not happening when the application terminates, your old approach never worked, without you ever realizing. So are you sure that you need this kind of cleanup?Turbosupercharger
C
17

Java 9 introduces the Cleaner and Cleanable utility classes which take care of wiring up phantom references to a queue and a cleaning thread draining that queue.

While this lets you separate out the witness that will perform a post-mortem cleanup after the owning object has died, all the caveats about GC-triggered resource management still apply, i.e. it is still preferable to rely on AutoClosable and try-with-resources blocks to manage the lifecycle of resources, not on the garbage collector.

Chickenlivered answered 13/12, 2017 at 22:30 Comment(2)
Decent example enyo.de/fw/notes/java-finalization-revisited.htmlChiliad
Belated acceptance of this answer, since this is what we eventually implemented. (Our biggest problem with resource cleanup turned out to be cases where in effect we were passing a closeable stream as the result of an API call, and the client was neither reading the stream to completion, nor closing it.)Dentilingual
R
9

Phantom references are general replacement for finalize(). Many classes from Java runtime are already using them.

Using Phantom references is a bit laborious, you have to maintain own reference list and postmortem processing thread. On the other hand you are fully in control.

Here is a simple example of Phantom reference setup.

This article explains and compares implementation of Java finalize() and Phantom references.

Randalrandall answered 12/12, 2017 at 3:44 Comment(2)
Thanks. The article cited is about the most readable explanation of phantom references I've seen; the two things that are clear after reading around are (a) that people find them very hard to explain, and (b) that to make them work you have to replace a one-line finalize() method with an elaborate structure of about 6 separate classes. (One can assume that if the github example is considered "simple", then this isn't going to be easy.) Not encouraging, but thanks!Dentilingual
Well, best approach would be not to couple your object lifecycle with GC. Usage of special reference (finalizers, phantom, etc) should considered as last resort. Unfortunately, some times you have to go that hard way.Randalrandall
A
4

You can add a Thread as shutdown hook to the Runtime:

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    // cleanup code
}));

This is called when the VM terminates, so it should be a good replacement for finalize in your particular case

Assiduous answered 11/12, 2017 at 23:23 Comment(1)
It would work in many scenarios. But there are some scenarios where the VM will continue to do other things after it's finished running my application. And of course I don't want to lock the Configuration object in memory until the VM finishes.Dentilingual
Q
2

IMHO it's not the responsibility of your application to tell the logger to clean up its own mess. The logger itself can and should do it as (unlike IO streams or DB connections), it's supposed to live long.

But you already provide logger.close()... OK, then I'd suggest the following:

  • Ensure that close is idempotent, i.e., closing the logger the second time is a no-op.
  • Use both Runtime#addShutdownHook and a PhantomReference, and let them both call logger.close(), so that it gets called both when the JVM terminates or your application gets GC'ed.
Qualifier answered 12/12, 2017 at 21:28 Comment(1)
does the suggestion of using Runtime#addShutdownHook resolves the configuration object being locked in memory as pointed by Michael's comment to Lothar's answer. How can that be tacked? Thanks.Piave

© 2022 - 2024 — McMap. All rights reserved.