In the old C++ days memory management slowed development time considerably because every programmer needed to retain and later free the memory they allocated. Java automatically garbage collects (frees) unreferenced objects, which at first glance is useful. But this automatic garbage collection will not occur on objects that are still referenced.
The same goes for system resources. If a class temporarily uses a FileInputStream to do its construction, but forgets to close the stream and holds onto the file handle, the application will soon run out of allowed file handles because the owning object still “needs” the file according to the JVM. See the [basic.close] rule for a more detailed explanation of this problem.
If a class forgets to tell a database that it has finished using a result set, the database will usually hold the cursor open indefinitely, eventually producing an “out of cursors” error.
So the most common ways to leak resources are as follows:
Leaked |
How |
---|---|
Memory |
Objects are added to a Collection without any way for them to be removed from it. Eventually the process will run out of memory and crash. |
File Handles (referenced) |
A class retains an InputStream or OutputStream and forgets to close it. Eventually the Operating System will stop giving out new file handles to the Java process. No new files will be able to be opened for reading or writing - not even temporary files. The process will crash. |
File Handles (not closed) |
A piece of code opens a stream but fails to include a finally { } block to close the stream. The stream does not get closed until the owning object is garbage collected which may be 24 hours away - or never. The system may eventually run out of file handles if garbage collection does not occur. |
Database Connections |
When an unexpected problem occurs executing the SQL query, a connection is not returned to the connection pool in a finally { } block. Eventually the connection pool runs out of connections – the application will block forever in the hope that a connection becomes available. |
Database Cursors |
resultSet.close() is not called in a finally { } block, so when an unexpected problem occures, the database thinks it still has a client connection in progress. Eventually the database runs out of cursors and all other applications querying the database fail. |
Sockets |
A class opens a TCP/IP socket, but fails to include a finally { } block to force the socket to close in all cases. Or worse still, the class keeps the socket open forever using a reference. Eventually the operating system reaches the limit of open sockets and stops supplying them to Java. The application stops connecting to other systems. |
These examples extend to all kinds of resources that Java can manipulate. A web service, a transaction, an operating system, a messaging layer. Care must be taken with these resources to ensure a single finally {..} clause cleans up after unexpected or normal program behaviour.
The unsafe way of using resources is as below.
InputStream in = new FileInputStream(invoiceFilePath);
loadInvoiceFile(in);
in.close();
The safe way is to use a finally {..} block to guarantee that resources are returned.
InputStream in = null;
try
{
in = new FileInputStream(invoiceFilePath);
loadInvoiceFile(in);
}
finally
{
// this code always gets executed.
try {if (in != null) in.close();}
catch(IOException e){log.error(e);}
}
Tip: The class that opens the stream closes the stream. To be more specific the code that opens a stream or borrows a resource owns it, and has the responsibility of making sure it is closed safely in a finally { } block. Don't return a stream or pass it to a function because the programmer will assume it is not theirs to close.