Resource leak: 'in' is never closed, though it IS closed
Asked Answered
G

6

10

I know that there are a couple of similarly entitled questions out there, but most of them have simply forgotten to put a close() directive on their stream. This here is different.

Lets say I have the following minimal example:

public void test() throws IOException
{
    InputStream in;
    if( file.exists() )
    {
        in = new FileInputStream( file );
    }
    else
    {
        in = new URL( "some url" ).openStream();
    }
    in.close();
}

This give me a Resource leak: 'in' is never closed warning in Eclipse (Juno SR1). But when I move the in.close() into the conditional block, the warnings vanishes:

public void test() throws IOException
{
    InputStream in;
    if( file.exists() )
    {
        in = new GZIPInputStream( new FileInputStream( file ) );
        in.close();
    }
    else
    {
        in = new URL( "some URL" ).openStream();
    }
}

What is going on here?

Gummous answered 10/1, 2013 at 10:27 Comment(5)
Warning where. IDE? Java compilation? Which IDE? Which version?Dorcy
"...the warnings vanishes:". Which warnings?Algeciras
You ought to have a try/finally block. Close the stream in a finally block to make sure you don't miss out if an exception is thrown.Cheek
Whoops, removed a paragraph by accident. Edited the original post to fix it. The warning is Resource leak: 'in' is never closedGummous
@Cheek If an exception is thrown by the constructor, than I have no stream I could close at all! It is null then.Gummous
C
5

Here's how I'd write it:

public void test() throws IOException
{
    InputStream in = null;
    try {
        if(file.exists()) {
            in = new FileInputStream( file );
        } else {
            in = new URL( "some url" ).openStream();
        }
        // Do something useful with the stream.
    } finally {
        close(in);
    }
}

public static void close(InputStream is) {
    try {
        if (is != null) {
            is.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Cheek answered 10/1, 2013 at 10:35 Comment(5)
Thanks, this really does work. However, I still don't understand how it is going to happen that in != null AND an exception is thrown by the FileInputStream constructor. Lets assume, the FileInputStream constructor throws an exception. This implies that in is still null. Therefore, the is.close() statement is ignored either way?Gummous
I'd prefer an upvote and acceptance to your thanks. Your example is contrived, because you're doing nothing else but calling the constructor. But if you were actually using the InputStream it could throw an exception if the read fails. And nothing is "ignored". That's not how programs work.Cheek
Starting with Java 7, you can just use try with resources.Counselor
This is bad answer because if there is nothing to do then it should not throw resource warning, it is a bug in the compilerEphemeron
"throw resource warning"? What are you talking about? The close method throws a checked exception - you have no choice but to deal with it. Why don't you post your answer so we can see what you have to say? This question is almost 8 years old. Is there nothing you can do to boost your rep than finding old answers and posting trollish comments?Cheek
P
7

Because of the IO exception, you can run into a resource leak (poentially)

Try doing the following:

public void test() throws IOException
{
    InputStream in= null;
    try {
        if( file.exists() )
        {
            // In this case, if the FileInputStream call does not
            // throw a FileNotFoundException (descendant of IOException)
            // it will create the input stream which you are wrapping
            // in a GZIPInputStream (no IO exception on construction)
            in = new GZIPInputStream( new FileInputStream( file ) );
        }
        else
        {
            // Here however, if you are able to create the URL
            // object, "some url" is a valid URL, when you call
            // openStream() you have the potential of creating
            // the input stream. new URL(String spec) will throw
            // a MalformedURLException which is also a descendant of
            // IOException.
            in = new URL( "some url" ).openStream();
        }

        // Do work on the 'in' here 
    } finally {
        if( null != in ) {
            try 
            {
                in.close();
            } catch(IOException ex) {
                // log or fail if you like
            }
        }
    }
}

Doing the above will make sure you've closed the stream or at least made a best effort to do so.

In your original code, you had the InputStream declared but never initialized. That is bad form to begin with. Initialize that to null as I illustrated above. My feeling, and I'm not running Juno at the moment, is that it sees that the InputStream 'in', may potentially make it through all the hoops and hurdles to get to the point at which you are going to use it. Unfortunate, as someone pointed out, your code is a bit dodgy for an example. Doing this as I've detailed as well as @duffymo you'll get rid of the warning.

Panchito answered 10/1, 2013 at 10:33 Comment(1)
Thanks, this works. However I have an understanding problem with this solution. It is the same as with this answer here: link Can you explain this?Gummous
C
5

Here's how I'd write it:

public void test() throws IOException
{
    InputStream in = null;
    try {
        if(file.exists()) {
            in = new FileInputStream( file );
        } else {
            in = new URL( "some url" ).openStream();
        }
        // Do something useful with the stream.
    } finally {
        close(in);
    }
}

public static void close(InputStream is) {
    try {
        if (is != null) {
            is.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Cheek answered 10/1, 2013 at 10:35 Comment(5)
Thanks, this really does work. However, I still don't understand how it is going to happen that in != null AND an exception is thrown by the FileInputStream constructor. Lets assume, the FileInputStream constructor throws an exception. This implies that in is still null. Therefore, the is.close() statement is ignored either way?Gummous
I'd prefer an upvote and acceptance to your thanks. Your example is contrived, because you're doing nothing else but calling the constructor. But if you were actually using the InputStream it could throw an exception if the read fails. And nothing is "ignored". That's not how programs work.Cheek
Starting with Java 7, you can just use try with resources.Counselor
This is bad answer because if there is nothing to do then it should not throw resource warning, it is a bug in the compilerEphemeron
"throw resource warning"? What are you talking about? The close method throws a checked exception - you have no choice but to deal with it. Why don't you post your answer so we can see what you have to say? This question is almost 8 years old. Is there nothing you can do to boost your rep than finding old answers and posting trollish comments?Cheek
S
4

I suspect the warning is incorrect. It could be checking you are closing the stream in the same scope. In the second case, you are not closing the second stream.

Syllepsis answered 10/1, 2013 at 10:31 Comment(1)
I agree with this answer and I have upvoted the pointless and unexplained downvote. The warning is clearly incorrect. The second example where there really is a missing close proves the point.Asarum
C
0

Your in stream may not be initialized if the file doesn't exist and you try to close a non-existent file.

Your second example would also need a close statement to avoid leaks.

Cryptography answered 10/1, 2013 at 10:33 Comment(1)
No. If 'in' cant be initialized an exception is thrown and in.close() is not reached. It is not possible for this code to 'try to close a non-existent file'.Asarum
U
0

This same Eclipse reporting can happen when you explicitly throw an exception after you have opened your resource like:

public void method() throws IOException {
   BufferedReader br = new BufferedReader(new FileReader("myfile.txt"));
   while (br.ready()) {
      String line = br.readLine():
      if (line.length() > 255) {
         throw new IOException("I am some random IOException");
      }
   }
   br.close();
}

This is some contrived code for demonstration purposes so don't look too hard.

If one were to comment out the line, the warning goes away. Of course, you instead want to make sure that that resource is being closed properly. You could do:

if (line.length() > 255) {
   br.close();
   throw new IOException("I am some random IOException");
}

Do not rely on the Eclipse warnings in this case though. Get in the habit of using the try/finally approach to make sure that resources are correctly and consistently being closed.

Underpants answered 9/5, 2014 at 14:26 Comment(0)
D
0

I have something like:

InputStream content = httpResponse.getEntity()==null?null:httpResponse.getEntity().getContent();

that gives the same warrning. But if I leave it just like this:

InputStream content =httpResponse.getEntity().getContent();

I receive no warrnings. Isn't strange or what?

-- I hope my info is adding knowledge to the original question. Thanks!

Delrosario answered 23/6, 2015 at 7:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.