It's called garbage collection, not resource collection!
Java's garbage collector has made life so easy for us developers that we sometimes confuse between deleting a reference, i.e. a task the GC does, and releasing a resource - a task the GC doesn't do. That's because the GC does something else for us called
object finalization, where resources usually clean themselves up.
When I say resources, just think of
input/output streams, readers/writers,
channels, JDBC, JMS.. the list could go on and on. I almost expect to see code such as:
void copy(String source, String target) {
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target);
// do the copying
}
But wait! Where's the
close() call?! It should
actually look like this:
void copy(String source, String target) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(source);
fos = new FileOutputStream(target);
// do the copying
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException) {
// do nothing.
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException) {
// do nothing.
}
}
}
}
Not as pretty, but the resources are closed at the end of it! Notice the usage of
finally to ensure that the resources are closed regardless of thrown exceptions. Also notice how each
close has its own try-catch block; this is to ensure that even if the first resource wasn't closed properly, the second one will get its chance.
Closeable objects
For some odd reason, J2SE 5.0 introduced the
Closeable interface, which defines the "close" method. I say oddly, because one would expect something would have been done with it. Maybe static analysis in the compiler via a flag, perhaps an automatic closing system, maybe a new keyword to handle
Closeable objects like the
using keyword in .NET. Nothing of that, though. Not in J2SE 5.0, nothing in Java SE 6, and JDBC and JMS are stuck with their own exception types (which do not extend
IOException) so they didn't even get a maintenance version to make their interfaces
Closeable too!
Neverthelss, it does allow removing some of the boiler plate code. Consider the following lines of code from the previous example:
if (fis != null) {
try {
fis.close();
} catch (IOException) {
// do nothing.
}
}
These seem pretty generic for any
Closeable object. We could easily create a utility method
close(Closeable c) which works for any
Closeable, reducing quite a lot of redundant code!
Automatically closing pool
Named after NeXTStep/Cocoa's
NSAutoreleasePool class, I created a small utility class which allows you to register
Closeable objects to it, and then close all of them together - as the pool is
Closeable too!
If you're lazy, it uses the
PhantomReference technique I describe
elsewhere to close all your objects when it is garbage-collected. This way there will be
less overload on the finalization queue by shortening its work, as the pool will close all the objects itself outside of the GC threads.
Notice, though: It is not a good practice to leave the
AutoclosePool "open"; you should always close it, just like any resource! It just knows how to take care of itself if you don't.
With AutoclosePool the previous example code looks like the following:
void copy(String source, String target) {
AutoclosePool pool = new AutoclosePool();
try {
FileInputStream fis = pool.use(new FileInputStream(source));
FileOutputStream fos = pool.use(new FileOutputStream(target));
// do the copying
} finally {
pool.close(); // notice that the pool doesn't throw IOException.
}
}
Some points for thought
- I mentioned that the
finalize method closes the resource, and it's not always true. In some cases, you have to close the resource yourself or it will never be freed. This could be true for object pools (I certainly remember a version of DBCP that worked like this, but it might be slightly more automatic now) or implementations that use JNI and rely on the user to close the resource themselves for reasons starting from negligence towards some smartass design.
- I also mentioned that JMS and JDBC are stuck with their own exception types, but it's not all true: they could still implement the interface, and ignore the directive to throw that exception. It's ugly as hell, but it would work and let Connection, Statement, Session, Queue and more classes join the closeable party..
- I can see advantages to using the AutoclosePool technique when you have a lot of closeable objects - NIO is a classic example where you might have two or more Channels open at the same time. Instead of having to worry about closing each and every one of the channels, just
use() them and let the pool close them at the end of the method, regardless of how many of them you have added or removed while writing the method.
- Update: since it came up in comments often, I'm mentioning it here as well. Notice the comment next to AutoclosePool's
close method. It should disturb some of you that the class "swallows" exceptions. However, there are two good reasons for it: first, since it uses PhantomReferences to close other AutoclosePools in its close, use or constructor, the exception thrown might not belong to the current AutoclosePool but to another's. Second, as Guillaume mentions in the comments as well, throwing an exception from a finally clause is a bad habit as it could over-shadow an exception thrown from your try clause. In addition to all that, I implemented the ability to set an ExceptionHandler to the AutoclosePool, so that when an exception occurs it can handle it within the context of the exception instead of the context of calling use or whatever.
I'd love to hear comments about this, so please! Go ahead!
Comments (8)
AutoclosePool,use()ing a resource orclose()ing a pool it would clean previously unclosed pools. This allows for the pool to be safely discarded if forgotten. However, it also meant thatclose()doesn't guarantee that the exceptions thrown are your exceptions and not some other pool's exceptions. This could be mended of course, but because of the rest of the behavior, I thought it would be easier to just hide the exceptions altogether. To solve this, I did add an optionalExceptionHandlerwhich receives the exceptions when they occur; this way, whenever an exception does occur it would be notifying the application in the correct context (a handler's context). Do you think a different design is preferred?finallyclause since I want all the resources to close. I admit I didn't know of the "multiple-finally" technique, but you have to admit it's dead-ugly!