Tuesday, 17 May 2011

Are finalize() Methods Useful?

Under normal circumstances, the finalize method is not of much use when programming everyday java code. Indeed, Joshua Bloch states, in his book Effective Java, that "finalizers are unpredictable, often dangerous, and generally unnecessary"; given this view, it's not surprising that the section of the book I'm referring to is called 'Item 6: Avoid finalizers' (see Page 20).

Having said don't use finalizers, Bruce Eckel came up with a very good couple of exceptions in his book Thinking in Java. They're supposed to be used to free memory allocated by native calls to either C or C++ code. As C/C++ code does not have garbage collection, this allows us to call free() or delete() avoiding memory leaks.

Another use is to check that your objects are being freed when you're sure that they're ready to be freed and not in error. This is the premise of the 'termination condition'. In this example, we create class that's supposed to read part of a file, this is the ImpracticalFileHandler below:

  class ImpracticalFileHandler {

   
/** Pick any old file for the test */
   
private static final String FILE_NAME = FILE_PATH + "\\wibble.txt";

   
private FileInputStream fis;

   
private boolean isFileOpen;

   
private String testName;

    ImpracticalFileHandler
(String testName) {

     
try {
       
this.testName = testName;
        fis =
new FileInputStream(FILE_NAME);
        isFileOpen =
true;
     
} catch (Exception e) {
       
e.printStackTrace();
     
}
    }

   
void readSectionOfFile() {

     
try {
       
// This doesn't so anything except discard what it reads
       
byte[] b = new byte[30];
        fis.read
(b);
     
} catch (Exception e) {
       
e.printStackTrace();
     
}
    }

   
/** Close the file - Don't call this in Example 2 */
   
void closeFile() {

     
try {
       
fis.close();
     
} catch (Exception e) {
       
// Catch all exception and display a message
       
e.printStackTrace();
     
} finally {
       
isFileOpen = false;
     
}
    }

   
@Override
   
protected void finalize() {

     
if (isFileOpen)
       
System.out.println(testName + " Error - You've left the file open");

   
}
  }

To demonstrate the finalizer, create two instances of this class, and read some of the file. In the first instance, DO NOT call the closeFile() method, leave the file open. Only close the file for the second instance.

  public static void main(String[] args) {

   
// EXAMPLE 1 - This should create an error message
   
ImpracticalFileHandler test = new ImpracticalFileHandler("Example 1");

   
// Do something with the file.
   
test.readSectionOfFile();

    test =
null; // drop the reference

    // EXAMPLE 2 - This won't create an error message
   
test = new ImpracticalFileHandler("Example 2");

   
// Do something with the file.
   
test.readSectionOfFile();

    test.closeFile
(); // tidy up correctly

   
test = null; // drop the reference

    // Ask the system to collect garbage - it may ignore us
    // We should get one message if all goes well
   
System.gc();
 
}

In running this example, the message may, or may not, be printed as System.gc() isn’t guaranteed to run the garbage collector when asked. I find that System.gc() is called when you step through the code using the eclipse debugger.

This is a somewhat artificial example as, yes, I know that FileInputStream has a finalize() method to close the file automatically if the programmer forgets, but it proves that the finalize method does do something.

No comments: