Thursday, July 7, 2022

Right way to Close InputStream and OutputStream in Java - Example

For some unknown reasons many Java programmers are not very comfortable with IO package. I don't know why, but I have found them much more comfortable with java.lang and java.util than java.io. One possible reason of this could be that, writing IO code require a bit of C++ like programming, which involves doing clean-up, releasing resources once done etc. Since Java made coding a lot easier by taking care of memory management, unknowingly it also introduced bad practice of not releasing resource after use like database connections, socket connection, files, directory, printers, scanners or any other scarce resource.

The laziness of just doing work and forget everything is very easy, because of this many Java programmer never bother about doing clean-up. This habit is most visible in programmers who have never done system programming using C or C++.

Since IO requires you to deal with streams, channels, and file descriptors, which need to be closed properly, Java developer find it uneasy to deal with. On other day, I asked one candidate to write code for copying content of one file to another without using copy() method or a third-party library. Though he managed to write the code, he made a common mistake, he was not closing streams properly.

It's important to close streams, to release file descriptor held by this class, as its limited resource and used in both socket connection and file handling. A serious resource leak may result in file descriptor exception as well.




How to close InputStream and OutpuStream in Java

Before moving ahead, let's see the part of  the code a programmer wrote for copying file from one directory to another directory in Java without using any third-party library.

FileInputStream fis = null;
FileOutputStream fos = null;

try {

    fis = new FileInputStream("../input/fxrates.txt");
    fos = new FileOutputStream("../output/fxrates.txt");

    // code for reading from input stream and writing to output stream

} finally {

    try {   
        // He was careful to close streams in finally block, 
        // but it’s not complete
        // Can you spot error?

        if(fis != null) fis.close();
        if(fos != null) fos.close();

    } catch(IOException e) { System.out.println("Failed to close streams");  }

}
Most of his code is al-right and even better than many Java programmers. He was even careful to close streams in finally block, but he still made an error, which could cause resource leak in his Java program. Can you spot the error? Yes, output stream will not be closed if close() method of input stream will throw an Exception i.e. fos.close() will not even execute if fis.close() throws exception. 

This means file descriptor held by OutputStream will never release causing a resource leak in Java program. It's not uncommon, I have seen many such code, where developers has right intention to release resources by closing streams but fail to realize something equally important. 

Right way of closing stream is by closing them in their own try catch block, so that failure of closing one stream should not prevent calling close() on other stream. 

Here is the right way of closing InputStream and OutputStream in Java :
InputStream is = null;
OutputStream os = null;

try {

    is = new FileInputStream("../input/fxrates.txt");
    os = new FileOutputStream("../output/fxrates.txt");

    ......

} finally {

    try { if (is != null) is.close(); } catch(IOException e) {//closing quietly}
    try { if (os != null) os.close(); } catch(IOException e) {//closing quietly}

}
How to Close InputStream and OutputStream in Java properly


This code will not forget to call os.close() even if is.close() will throw IOException, which ensures that file descriptor held by OutputStream will be released. If you don't like so many try-catch and try-finally block or fed-up with verbosity of this program then you can also try Apache commons IO package.

It provides a closeQuitetly() method to close streams quietly i.e. above finally block can be re-written by using IOUtils.closeQuietly() as following.
try{
   .......
   ........
} finally {
    IOUtils.closeQuietly(in);
    IOUtils.closeQuietly(os);
}
closeQuitely() is an overloaded method for closing URLConnection, Closable, Socket, ServerSocket, Selector, InputStream, OutputStream, Reader and Writer classes. It is also null-safe, so don't check if Stream is null before calling this method. Here is source code of closeQuitely() method for closing InputStream :

 public static void closeQuietly(InputStream input) {
        try {
            if (input != null) {
                input.close();
            }
        } catch (IOException ioe) {
            // ignore
        }
}
By the way, you have a much better alternative if you are using Java 7. It has provided try-with-resource statements for automatic resource management in Java. All resources opened in try block will automatically closed by Java, provided they implements Closable and AutoClosable. Since all InputStream and OutputStream are eligible to be used inside try-with-resource statements, you should take advantage of that. 

This is really great for Java programmer, as they are not as careful as their C++ counterparts, especially while releasing resource. 

Here is how does above code look like with try-with-resource statement.

try (FileInputStream fis = new FileInputStream("../input/fxrates.txt");
      FileOutputStream fos = new FileOutputStream("../output/fxrates.tx")) {

      // code for reading contents

      .....

 } catch (IOException ioex) {
   System.out.println("Failed to copy files : " + ioex.getMessage());
   ioex.printStackTrace();
 }
As you can see, we have got rid of lot of boiler plate try-finally code. Since you can declare more than one resource inside try-with-resource block, allocate your streams, channels, and readers there.

That's all on this post about right way of closing InputStream and OutputStream in Java. We have seen three examples of closing streams in Java and how combining close() call of two stream can cause resource leak in Java. Take away is always close streams in their own try-catch block. 

If you are using Apache commons IO in your project then take advantage of IOUtils.closeQuietly() method to reduce boiler-plate code. Prefer try-with-resource over manual handling of resource in Java 7. 

Bottom line is all opened streams must be closed once you are through with them. This rule applies to all resources like database connections, network connections, files, printers and any other shared resource. You must release once you are done.

Other Java File Tutorials and Examples 
If you like this article and love to read more about InputStream, Files and OutputStream in Java, see these amazing articles :

Thank you for reading this Java File IO tutorial so far. If you like this Java best practice then please share with your friends and colleagues. If you have any questions feel free to ask in comments. 

8 comments :

SARAL SAXENA said...

@Javin..As now Java 7 have come up so its best to take advantage of Java 7 now...of try with resources..

try(BufferedReader r = new BufferedReader(new InputStreamReader(address.openStream()))) {
String inLine;
while ((inLine = r.readLine()) != null) {
System.out.println(inLine);
}
} catch(IOException readException) {
readException.printStackTrace();
}

javin paul said...

Indeed Saral, I think I have mentioned about try with resource statement and automatic resource management. That's the best way to use Streams, Channels or any network resource.

Anonymous said...

I still think your java 6 examples are doing it wrong and you are advocating a bad 1.6 pattern. You shouldn't be using closeQuietly() instead of close() but in addition to it. Here's an example:

public void test()
{
FileInputStream fis = null;
FileOutputStream fos = null;

try {

fis = new FileInputStream("../input/fxrates.txt");
fos = new FileOutputStream("../output/fxrates.txt");

// Omitted code for reading from input stream and writing to output stream
fos.flush();
fos.close();
fis.close();
} catch (IOException e) {
// The write, flush or close could have thrown the exception
// this is your chance to log it, wrap it, rethrow etc.
// you probably want to have a log message if there was a problem during the close.
logger.log(Level.WARNING, "An IOException was thrown while writing really important data", e);
} finally {
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(fis);
}

}

Veekshith said...

It worked. Always close the streams within individual try-catch blocks (JDK 6 and below). Thanks for wonderful post.

Anonymous said...

There is a reason finally block exists in Java. It's there to handle these kind of scenarios. If you are not running on JDK 1.7, you must close, destroy or release files, connections, objects, locks on finally block.

Amit said...

Hi,

Need some urgent clarification. I am working on a memory leak that occurred in a really old web service project. On going through the logs and memory dumps, I came across this file which I thought could be a a potential memory leak.

The code causing the issue is as follows

public void doPost(final HttpServletRequest req,
final HttpServletResponse resp)
throws ServletException, IOException {
//Some code here
resp.setContentType(contentType.toString());
final ByteArrayOutputStream bos = new ByteArrayOutputStream();

InputStream is = null;
OutputStream os = null;
try {
is = new BufferedInputStream(req.getInputStream());
os = new BufferedOutputStream(resp.getOutputStream());
handle(is, contentType, bos, req, resp);
final byte[] content = bos.toByteArray();
resp.setContentLength(content.length);
os.write(content);
} catch (Throwable th) {
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, th.getMessage());
LOG.log(Level.WARNING, th.getMessage(), th);
} finally {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
}
}

Now here's my question, after looking through the documentation on ObjectOutputStream, and some other blogs on performance tuning, people are recommending that I flush the ObjectOutputStream before I close it. Do you think adding the line

os.flush();

in the finally block will plug this memory leak?
Basically flushing out the stream should ease the load on the heap space right?

Anonymous said...

Thank you!

Jitendra said...

Hi ,
I have one question let say we have one rest service which is returning InputStream as response of rest service like below , how do we close it at client side .

return Response.ok(requireNonNull(getClass().getClassLoader().getResourceAsStream("temp/abc.test"))).build();

Post a Comment