Java 101: Trash talk, Part 1

Java recycles its memory through garbage collection

1 2 3 4 5 Page 4
Page 4 of 5

For your first example of a program that overrides finalize(), check out Listing 1:

Listing 1. FinalizeDemo1.java

// FinalizeDemo1.java
class FinalizeDemo1
{
   public static void main (String [] args)
   {
      new FinalizeDemo1 ();
   }
   public void finalize () throws Throwable
   {
      System.out.println ("finalize() called");
      super.finalize ();
   }
}

FinalizeDemo1 is a simplistic application. Its source code includes a finalize() method that prints a message and, when run, calls super.finalize (); to allow its superclass to release any acquired finite resources. That is necessary because the garbage collector does not automatically call any superclass's finalize() method in a class hierarchy. Because FinalizeDemo1 inherits from Object, and because Object allocates no finite resources, you do not need to include super.finalize (); in FinalizeDemo1's finalize() method. However, you should end your finalize() methods with calls to super.finalize ();. During the class hierarchy design, you might insert a superclass -- one that declares its own finalize() method -- above the class that contains finalize(). (Listing 1 requires super.finalize (); because of the throws Throwable clause.)

Tip
Get into the habit of ending your finalize() methods with super.finalize(); method calls. That way, if you insert a superclass above your class and if that superclass has its own finalize() method, you ensure that the garbage collector calls the superclass's finalize() method.

Let's look at FinalizeDemo1's execution. When run, FinalizeDemo1's main() method creates a FinalizeDemo1() object and promptly discards that object's only reference, thus making that object unreachable. As a result, the object becomes eligible for garbage collection. However, finalize() does not run (at least on my platform) because the garbage collector only runs at intervals, and the program exits before the garbage collector has a chance to run. As a result, one unreachable FinalizeDemo1 object remains on the heap when the program exits. That behavior proves that finalize() is not Java's equivalent of a destructor. After all, a language must guarantee that a call is made to every object's destructor (when that destructor is present). In contrast, Java does not guarantee that a call is made to every object's finalize() method.

How do you get the garbage collector to run? Remember System.gc ();? You can increase the possibility of the garbage collector running by inserting that method call into FinalizeDemo1's main() method, which is what Listing 2 accomplishes:

Listing 2. FinalizeDemo2.java

// FinalizeDemo2.java
class FinalizeDemo2
{
   public static void main (String [] args)
   {
      new FinalizeDemo2 ();
      System.gc ();
   }
   public void finalize () throws Throwable
   {
      System.out.println ("finalize() called");
      super.finalize ();
   }
}

Does the garbage collector run now? Usually when I run FinalizeDemo2 on my Windows 98 SE platform using Sun's Java 2 Platform, Standard Edition (J2SE) SDK 1.4, I see the following output:

finalize() called

The garbage collector runs finalize(). If you do not see the output on your platform, keep in mind that, for whatever reason, your JVM might not run the garbage collector. That means you should not rely on finalize() to release finite resources. You should make every effort to explicitly release those resources; consider finalize() as only a fallback mechanism.

There is a greater probability that the garbage collector will recycle an unreachable object after calling its finalize() method in either a continuously running program -- like a Web server program -- or a long-running program that creates many objects. To see that for yourself, compile and run Listing 3:

Listing 3. FinalizeDemo3.java

// FinalizeDemo3.java
class FinalizeDemo3
{
   private static int globalID;
   private int localID;
   FinalizeDemo3 ()
   {
      localID = globalID++;
      System.out.println ("Initializing object #" + localID);
   }
   protected void finalize () throws Throwable
   {
      System.out.println ("Finalizing object #" + localID);
      super.finalize ();
   }
   public static void main (String [] args)
   {
      FinalizeDemo3 fd;
      for (int i = 0; i < 10000; i++)
           fd = new FinalizeDemo3 ();
   }
}
1 2 3 4 5 Page 4
Page 4 of 5