finalize() methods are as old as Java. But its implementation details and performance implications remain misunderstood (if not unknown) by many Java developers.
At Devoxx US on the 22nd March, Gautam Singh will be giving his Finalizers – The not so good, the bad the ugly talk. He will share his encounter with Finalizers, including how he rescued a retailer from daily server restarts in peak hours.
We asked Gautam about Finalizers and his experiences.
Object.finalize() work under the hood?
Object.Finalize() method is called by the JVM after an object is identified as a candidate for Garbage Collection (GC). An instance of a class that implements the
finalize() method is often called a “finalizable object”. With the Hotspot JVM, the following describes what happens when we create an instance of a finalizable object:
- The JVM creates a new instance of finalizable object.
- This creates an instance of the
java.lang.ref.Finalizerpointing to the original finalizable object instance.
- It stores the newly created Finalizer instance as static reference with the
java.lang.ref.Finalizerclass. This is done to keep the instance alive, otherwise it will get collected in the next GC cycle.
During Garbage Collection, if a finalizable object is only referenced from the
java.lang.ref.Finalizer class, the JVM adds the finalizable object to a queue called the Finalizer Reference Queue. This queue is processed by a demon thread called Finalizer thread. The Finalizer thread picks the objects from the Finalizer queue and calls the
finalize() method on the object. After the
finalize() method is called on the object, the object memory space can be claimed in next GC cycle.
If this sounds too complicated for handling a simple method, you are not alone. I had the same thought when I stumbled across an issue with finalizers, and had to understand the inner workings of Finalizer. In my talk I will explain why it is implemented in such a convoluted way.
What are the main side effects of the
Because of the way Finalizable objects are handled by Garbage Collectors, it incurs a substantial overhead.
- The Finalizable object has a hidden allocation cost.
- It takes at least 2 GC cycles to collect a finalizable object.
- If not implemented correctly, finalizable objects can resurrect a dead object. This could cause a memory leak.
- There is no guarantee when the
finalize()method will be called – or if it will be called at all.
When is it appropriate to use a finalizer?
If it is possible to avoid finalizer, please do so at all costs. Finalizer should only be used to free up a non-memory resource like native heap, that cannot be cleaned up in any other possible way. A typical situation could be where a non-memory resource is embedded deeply inside a linked structure.
What is the worst abuse of
Object.finalize() that you’ve seen?
One of the anti-patterns I have seen used by a few Java programmers is to set object member references to null in a
finalize() method. This makes sense in C++ world, but is the worst possible thing one can do in JVM based language.
Modern GCs are very efficient in identifying the unused objects, so nulling is not only unnecessary, it is also counterproductive. It makes the object stay longer in memory.
For more, see Finalizers – The not so good, the bad the ugly at Devoxx US.