Search code examples
javagarbage-collectionfinalizerfinalizephantom-reference

Alternative to Java finalizer


I am implementing a locking service in a distributed system using Mysql GET_LOCK. Upon calling my getLock() method, if a lock is obtained by a client, I make an entry to DB and delete the entry when lock is released.

Assumption is calling client would release the lock once its purpose is served. However, I would like to ensure that lock gets released in case client does not release it or doesn't do proper cleanup.

One way would be to use finalize method on my lock object to release it when finalize is called. However it's not ideal, adds complications and is deprecated in Java 9. I read about Phantom references which are better than finalizers, but its complexity level is also high. I am keeping it as my last resort.

Is there any simpler, less JVM dependent way to handle this usecase?


Solution

  • Don’t do this. The problems outweigh the benefits.

    Regardless of whether you are using finalization, the Reference API, or a Cleaner (which settles on the Reference API), trying to use an object’s lifecycle to control a lock will create even more problems.

    There is no guaranty that an object will ever get garbage collected. As long as there is enough free heap memory left, a JVM has no reasons to let the garbage collector run. Further, even when a garbage collector runs, there is no guaranty that it will collect all unreachable objects. Garbage collectors like G1GC focus on reclaiming as much memory as they can within given time constraints, prioritizing effect over object ages. In fact, it’s not even known to them, how long a dead object is lying around.

    So a particular unreachable object may stay uncollected for an infinite time while the JVM is happy with the GC collecting a bunch of newer objects.

    Even worse, there is the possibility for an object to get garbage collected earlier than expected. For explicit lock and unlock actions, this has no impact, as the behavior of the program stays the same. But when you connect the object’s lifecycle with an unlock action, you’re in trouble. This applies especially, when the object only serves the lock & unlock actions and the unlock action has been forgotten by the application programmer, in other words, the lock object is entirely unused after performing the lock action.

    To prevent early collection, you need something like Reference.reachabilityFence(lockObject), but when the programmer forgets to do the required unlock action, how likely are they to remember to insert the necessary reachability fence? It would be easier for them to insert the required unlock action instead.

    Any attempt to solve this problems will be far away from being simple while still not providing guarantees that were worth the effort. You better remind the users of your lock class strongly on the need to unlock it. Consider implementing AutoCloseable and advertise using it in a try-with-resource block.