Before the garbage collector acts on an object to reclaim it, the object's finalizer is executed. This is required to ensure that resources such as open streams, files and network connections get freed since resource management does not happen automatically while reclaiming memory. In Java, the finalize
method of java.lang.Object
is used to perform this activity.
The caveats associated with the use of finalizers are discussed here:
- There is no fixed time for finalizers to get executed, which again is JVM dependent: The only thing that is guaranteed is that a finalizer will be executed before the garbage collector's run. An object may become unreachable and yet its finalizer may not execute for an arbitrarily long time. Nothing of time-critical nature should be run in the
finalize
method, for instance, closing file handles is not recommended.
- Do not depend on a finalizer for updating critical persistent state: It is possible for the JVM to terminate without invoking the finalizer on an unreachable object. As a result it is not advisable to use any lock or sharing based mechanisms within a finalizer. Methods such as
System.gc
,System.runFinalization
,System.runFinalizersOnExit
andRuntime.runFinalizersOnExit
are either just marginally better or have been deprecated due to lack of safety and deadlock causing effects.
According to the Java Language Specification: [[JLS 05]] Section 12.6.2:
The Java programming language imposes no ordering on finalize method calls. Finalizers [of different objects] may be called in any order, or even concurrently.
- Effect of uncaught exceptions: An uncaught exception thrown during finalization is ignored. The finalization process itself stops immediately so it fails to accomplish its purpose.
- Unintentional mistakes like memory leaks can also cause finalizers to never execute to completion.
- A possibility exists such that the programmer unintentionally resurrects the references in the
finalize
method. While the garbage collector must determine yet again whether the object is free to be deallocated, thefinalize
method is not invoked again.
- A superclass can use finalizers and pass some additional overhead to extending classes. An example from JDK 1.5 and earlier demonstrates this. The code snippet below allocates a 16 MB buffer for backing a Swing
Jframe
. None of theJFrame
APIs have afinalize
method, however,JFrame
extendsAWT Frame
which has afinalize
method. The byte buffer continues to persist until thefinalize
method gets called and lasts for at least two garbage collection cycles.
Class MyFrame extends Jframe { private byte[] buffer = new byte[16 * 1024 * 1024]; // persists for at least two GC cycles }
- It is also imprudent to use finalizers for reclaiming scarce resources by inviting garbage collection. Garbage collection usually depends on memory related traits and not on the scarcity of a particular resource. Thus, if memory is available aplenty, there is no reason for the scarce resource to not get exhausted despite the use of a finalizer. See FIO34-J. Ensure all resources are properly closed when they are no longer needed and CON02-J. Facilitate thread reuse by using Thread Pools for more on handling resources correctly.
Exceptions
OBJ02-EX1: Sometimes it is necessary to use finalizers especially while working with native objects/code. This is because the garbage collector cannot re-claim memory from code written in another language. Again, the native process must not perform any critical jobs that require immediate resource deallocation.
In such cases, finalize
should be used correctly. Any subclass that overrides finalize
must explicitly invoke the method for its superclass as well. There is no automatic chaining with finalize
. The correct way to handle this is shown next.
protected void finalize() throws Throwable { try { //... } finally { super.finalize(); } }
Alternatively, a more expensive solution is to declare an anonymous class so that the finalize
method is guaranteed to run for the superclass. This solution is applicable to public non-final classes. "The finalizer guardian object forces super.finalize
to be called if a subclass overrides finalize and does not explicitly call super.finalize
". [[JLS 05]] Section 12.6.1: Implementing Finalization.
public class Foo { // The finalizeGuardian object finalizes the outer Foo object private final Object finalizerGuardian = new Object() { protected void finalize() throws Throwable { // Finalize outer Foo object } }; //... }
If a superclass defines a finalize
method, make sure to decouple the objects that can be immediately garbage collected from those that depend on the finalizer. In the MyFrame
example, the following code will ensure that the buffer
doesn't persist longer than expected.
Class MyFrame { private JFrame frame; private byte[] buffer = new byte[16 * 1024 * 1024]; // now decoupled }
Risk Assessment
Finalizers can have unexpected behavior.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
OBJ02-J |
medium |
probable |
medium |
P8 |
L2 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[JLS 05]] Section 12.6, Finalization of Class Instances
[[API 06]] finalize()
[[Bloch 08]] Item 7, Avoid finalizers
[[Darwin 04]] Section 9.5, The Finalize Method
[[Flanagan 05]] Section 3.3, Destroying and Finalizing Objects
[[Coomes 07]] "Sneaky" Memory Retention
OBJ01-J. Understand how a superclass can affect a subclass 06. Object Orientation (OBJ) OBJ03-J. Be careful about final reference