Practical Java Praxis 67: Do Not Rely on finalize Methods for Nonmemory Resource Cleanup
The finalize method of a class is invoked by the JVM for an object of the class before the garbage collector reclaims its memory. This method is sometimes advertised as a way to ensure that nonmemory resources are freed before the memory for an object is reclaimed. Because the garbage collector frees only object memory, the finalize method provides a way to free other resources.
Programmers might place calls to close open sockets or file handles inside of a finalize method. The thinking is that this ensures that the program never runs out of these resources. In fact, the program can still run out of these resources regardless of the code in a finalize method.
The issue is that an object's finalize method is called before the garbage collector runs to free the storage for that object. Depending on the garbage collection algorithm used, possibly not all eligible objects will be collected on a given execution of the garbage collector. In addition, finalize methods are not guaranteed to run at predictable times. This is because of the likely asynchronous nature of finalization and garbage collection. Consequently, the finalize methods of objects are not guaranteed to run before the program terminates. This means that even though you have coded the finalize methods properly to free nonmemory resources, you can still deplete these resources before your program terminates.
One way to potentially avoid this problem is to use the System.runFinalization method. This method requests that the JVM invoke the finalize methods on all objects that have been identified as "finalizable" by a previous run of the garbage collector. Whether it does depends on the algorithms used in both the garbage collector and this method. However, there is no guarantee that invoking this method will run the finalize methods.
Another method previously available was the System.runFinalizersOnExit method, now deprecated. This method guaranteed only that the finalize method of all objects would run before the JVM exited. This meant that by the time the finalize method executed, you could already be out of resources. You typically want the finalize method to run during the execution of your program, not at the end, when the JVM is exiting. This method was deemed unsafe and is deprecated in Java 2.
Therefore, you cannot rely on the finalize method for an object being called. You should implement your own nonmemory resource cleanup mechanism that is used in conjunction with the class finalize method. You must ensure that classes that require such cleanup contain a public method that can be called to free the resources. This method should be called from the class finalize method to ensure that the nonmemory resources are freed if the finalize method is invoked. If the finalize method is not invoked, the user can call the public method provided by the class as a way to free these resources. Consider the following class:
class Communication { private ServerSocket ss; private FileInputStream fileIn; //... public synchronized void cleanup() throws IOException { if (ss != null) //Check for null, so we don’t call close on an { //already closed socket. ss.close(); ss = null; } if (fileIn != null) //Ditto { fileIn.close(); fileIn = null; } } protected void finalize() throws Throwable { try { cleanup(); } finally { super.finalize(); //Always call super.finalize in a finally } } //... }
This class provides a finalize method that invokes the cleanup method. If the finalize method is called by the JVM, the nonmemory resources are freed. Because the cleanup method is public, it also can be called by other code at any time. For example, you might implement a resource pool management framework that includes a cleanup method.
Depending on the design of your system, you should call the cleanup method of an object at regular intervals to ensure that you do not run out of the resources that the class manages. Because you might call it frequently, you must code it to handle multiple invocations properly. For example, notice that the cleanup method code is careful to check that the object references are not null before it calls their close methods. Furthermore, after the close methods are called, the object references are set to null. This ensures that multiple invocations of the cleanup method do not result in multiple calls to the close method. In addition, the cleanup method is declared synchronized. This guarantees that multiple threads do not enter this method concurrently for the same object.
The finalize method is also careful to call super.finalize from a finally block. All finalize methods should call super.finalize to ensure that any superclass finalize methods are invoked. Unlike superclass constructors that are invoked automatically, finalize methods must be chained manually. The super.finalize call is made from a finally block to ensure that it is called regardless of whether the call to the cleanup method generates an exception.
Because you cannot guarantee if or when a finalize method is called, you should provide a public method to perform nonmemory resource cleanup. This method should also be called by the class finalize method.
About the Author
Peter Haggar is a senior software engineer with IBM in Research Triangle Park, North Carolina, and author of the best-selling book Practical Java, published by Addison-Wesley. Having worked on development tools, class libraries, and operating systems, he has a broad range of programming experience. At IBM, Peter works on emerging Java technology and, most recently, on real-time Java. He is also a frequent technical speaker on Java technology at numerous industry conferences. Peter received his bachelor of science degree in computer science from Clarkson University. He can be contacted at haggar@us.ibm.com.