RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


How to Handle Java Finalization's Memory-Retention Issues

Finalization allows you to perform postmortem cleanup on Java objects, but it can delay the reclamation of resources, even if you do not use it explicitly. Learn how to avoid such memory-retention problems.

inalization is a feature of the Java programming language that allows you to perform postmortem cleanup on objects that the garbage collector has found to be unreachable. It is typically used to reclaim native resources associated with an object. The following is a simple finalization example:

public class Image1 {
	// pointer to the native image data
	private int nativeImg;
	private Point pos;
	private Dimension dim;

	// it disposes of the native image;
	// successive calls to it will be ignored
	private native void disposeNative();
	public void dispose() { disposeNative(); }
	protected void finalize() { dispose(); }

	static private Image1 randomImg;

Figure 1. A Finalizable Object obj

Some time after an Image1 instance has become unreachable, the Java virtual machine (JVM) will call its finalize() method to ensure that the native resource that holds the image data (pointed to by the integer nativeImg in the example) has been reclaimed. Notice, however, that the finalize() method, despite its special treatment by the JVM, is an arbitrary method that contains arbitrary code. In particular, it can access any field in the object (pos and dim in the example). Surprisingly, it can also make the object reachable again by, say, making it reachable from a static field (e.g., randomImg = this;). I really don't recommend the latter programming practice, but unfortunately the Java programming language allows it.

The following steps describe the lifetime of a finalizable object obj—that is, an object whose class has a non-trivial finalizer (see Figure 1):

Figure 2. Garbage Collector Determines That obj Is Unreachable

  1. When obj is allocated, the JVM internally records that obj is finalizable (this typically slows down the otherwise fast allocation path that modern JVMs have).
  2. When the garbage collector determines that obj is unreachable, it notices that obj is finalizable (as it had been recorded upon allocation) and adds it to the JVM's finalization queue. It also ensures that all objects reachable from obj are retained, even if they are otherwise unreachable, as they might be accessed by the finalizer. Figure 2 illustrates this for an instance of Image1.
  3. At some point later, the JVM's finalizer thread will dequeue obj, call its finalize() method, and record that obj's finalizer has been called. At this point, obj is considered to be finalized.
  4. When the garbage collector rediscovers that obj is unreachable, it will reclaim its space along with everything reachable from it (provided that the latter is otherwise unreachable).

Notice that the garbage collector needs a minimum of two cycles (maybe more) to reclaim obj and needs to retain all other objects reachable from obj during this process. If a programmer is not careful, this can create temporary, subtle, and unpredictable resource-retention issues. Additionally, the JVM does not guarantee that it will call the finalizers of all the finalizable objects that have been allocated; it might exit before the garbage collector discovers some of them to be unreachable.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date