Every class in Java has a finalize() method inherited from the Object class, which will be called by GC when the object is eligible for garbage collection. Main purpose of this method is to perform any cleanup actions before the object is completely discarded by GC. However, the finalize method may take any action, including making this object available again to other threads. The finalize method of class Object does nothing; it simply returns normally. Java does not guarantee which thread will invoke the finalize method for any given object, but is guaranteed, that the thread that invokes finalize will not be holding any user-visible synchronization locks when finalize is invoked. Java also guarantees that the finalize method is never invoked more than once by a JVM for any given object. If an uncaught exception is thrown by the finalize method, the exception is ignored and finalization of that object terminates.
According to the book Effective Java (which is one of the best Java books I have read), Finalizers are unpredictable, often dangerous, and generally unnecessary as they can cause erratic behavior, poor performance, and portability problems. Using finalize is not a good idea in most cases (as we will see soon), but even JDK uses it in some unavoidable situations (We will see examples and also some workarounds to achieve similar effect.)
Finalizers are bad for functional reasons as well as for performance reasons. We will first see functional reasons and then the performance reasons.
Functional reasons not to use finalize
You should never depend on a finalizer to update critical data or do anything time-critical in a finalizer, or assume that finalize will work the same on another JVM, because:
Java language specification does not guarantee that finalize will always be executed; specification only guarantees that finalize() will not be called twice. Even System.gc and System.runFinalization (though may increase the chances of finalizers getting executed,) don’t guarantee it either.
There is also no guarantee finalizers will be executed promptly; even after an object becomes eligible for garbage collection, it can take any time before finalizer is executed.
When to execute finalizer may also dependent on the GC algorithm in use in a particular JVM, thus also affecting portability.
Performance impact of finalize
Finalizers also has great impact on performance. The JVM uses a special private reference object class java.lang.ref.Finalizer to keep track of objects that have defined a finalize() method. The java.lang.ref.Finalizer in turn is a java.lang.ref.FinalReference. This is a special reference similar to other public reference object classes available in the java.lang.ref package. You can learn more about reference objects @ http://javajee.com/soft-and-week-reference-object-classes-in-java and for further reference you can refer to Oracle documentation for java.lang.ref package @ http://docs.oracle.com/javase/7/docs/api/java/lang/ref/package-summary.html.
When an object that has a finalize() method is allocated, the JVM allocates two objects: the object itself, and a Finalizer reference that also refer to this object. As with other less strong references, it takes at least two GC cycles before this less strong reference object can be freed. However, the performance penalty here is greater than with other less strong reference types. When the referent of a soft or weak reference is eligible for GC, the referent object is freed first, and the soft or weak reference is placed on the reference queue and is freed in next GC cycle; the two-cycle penalty for GC applies only to the reference object itself (and not the referent). However in case of finalizers, referent object is not freed immediately when the finalizer reference is placed on its reference queue, as the implementation of the Finalizer class must have access to the referent in order to call the referent’s finalize() method. Only when the reference queue processes the finalizer, the Finalizer object will be removed from the queue and then eligible for collection. Unlike other less strong references, here the memory of the referent object is also retained, which can be usually much bigger than the reference object.
However in some cases, finalize might be unavoidable. For instance, if you have some native resource to be freed, then you can have an explicit method to free that resource, but also can have finalize as a backup plan in case the developer forget to call that explicit method. For instance, classes that handle zip files in JDK uses this technique as opening a ZIP file uses some native code that allocates native memory and it needs to be closed even if developer forget to call close. Other JDK classes such as FileInputStream, FileOutputStream, Timer, Connection etc. also have finalizers. The book Effective Java suggests that the finalizer should log a warning in these cases if it finds that the resource has not been terminated, so that the client code can be fixed (however JDK classes don’t do that). You should also make sure that the memory accessed by the object is kept to a minimum in such cases when it is unavoidable to use finalizers.
Unlike constructors, finalize won’t automatically call its parent. So you need to call the super finalize explicitly (like any other method), and there is a change that you may miss that. The section ‘Item 7: Avoid finalizers’ in Effective Java talks about a solution to this problem: Put the finalizer on an anonymous inner class (called a finalizer guardian), and a single instance of the anonymous class is stored in a private instance field so the finalizer guardian becomes eligible for finalization at the same time as the enclosing instance. When the guardian is finalized, it performs the finalization activity desired for the enclosing instance, just as if its finalizer were a method on the enclosing class.
You can overcome some of the disadvantages of finalizers by using PhantomReference class (along with ReferenceQueue) rather than implicitly using a Finalizer reference. This can overcomes two limitations of the traditional finalizer: the memory associated with the referent object is released as soon as the referent is collected (rather than doing that in the finalizer() method and giving a chance to resurrect). There is also no way for the referent object to be resurrected in the cleanup code, since it has already been collected. Other limitations like no guarantee of timing, no guarantee that it will be actually cleared etc. can happen here also.
I will not explain the approach here, but just wanted to tell that there is one such approach possible. You can search online or refer to a book that explains the approach. I recommend you look at the book ‘Java Performance: The Definitive Guide’ as that is my primary reference book for learning java performance.
The finalizer queue is the reference queue used to process the Finalizer references when the referent is eligible for GC.
You can cause the finalizer queue to be processed by executing GC.run_finalization of jcmd command:
jcmd <process_id> GC.run_finalization
You can monitor the finalizer queue using jmap as:
jmap -finalizerinfo <process_id>
You can also monitor the finalizer queue using jconsole.