Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resource leak while using try...finally?

I was working normally in eclipse when I got bugged by a resource leak warning in both return values inside the try block in this method:

@Override
public boolean isValid(File file) throws IOException
{
    BufferedReader reader = null;
    try
    {
        reader = new BufferedReader(new FileReader(file));
        String line;
        while((line = reader.readLine()) != null)
        {
            line = line.trim();
            if(line.isEmpty())
                continue;
            if(line.startsWith("#") == false)
                return false;
            if(line.startsWith("#MLProperties"))
                return true;
        }
    }
    finally
    {
        try{reader.close();}catch(Exception e){}
    }
    return false;
}

I don't understand how it would cause resource leak since I'm declaring the reader variable outside the try scope, adding a resource inside the try block and closing it in a finally block using an other try...catch to ignore exceptions and a NullPointerException if reader is null for some reason...

From what I know, finally blocks are always executed when leaving the try...catch structure, so returning a value inside the try block would still execute the finally block before exiting the method...

This can be easily proved by:

public static String test()
{
    String x = "a";
    try
    {
        x = "b";
        System.out.println("try block");
        return x;
    }
    finally
    {
        System.out.println("finally block");
    }
}

public static void main(String[] args)
{
    System.out.println("calling test()");
    String ret = test();
    System.out.println("test() returned "+ret);
}

It result in:

calling test()
try block
finally block
test() returned b

Knowing all this, why is eclipse telling me Resource leak: 'reader' is not closed at this location if I'm closing it in my finally block?


Answer

I would just add to this answer that he's correct, if new BufferedReader throws an exception, an instance of FileReader would be open upon destruction by garbage collector because it wouldn't be assigned to any variable and the finally block would not close it because reader would be null.

This is how I fixed this possible leak:

@Override
public boolean isValid(File file) throws IOException
{
    FileReader fileReader = null;
    BufferedReader reader = null;
    try
    {
        fileReader = new FileReader(file);
        reader = new BufferedReader(fileReader);
        String line;
        while((line = reader.readLine()) != null)
        {
            line = line.trim();
            if(line.isEmpty())
                continue;
            if(line.startsWith("#") == false)
                return false;
            if(line.startsWith("#MLProperties"))
                return true;
        }
    }
    finally
    {
        try{reader.close();}catch(Exception e){}
        try{fileReader.close();}catch(Exception ee){}
    }
    return false;
}
like image 740
José Roberto Araújo Júnior Avatar asked Mar 06 '13 16:03

José Roberto Araújo Júnior


People also ask

What is try with resources in Java with example?

In Java, the Try-with-resources statement is a try statement that declares one or more resources in it. A resource is an object that must be closed once your program is done using it. For example, a File resource or a Socket connection resource.

Do we need to explicitly close the resources in try-with-resources?

We need not explicitly close the resources as JVM automatically closes them. This makes the code more readable and easier to write. We can declare more than one resource in the try-with-resources statement by separating them with a semicolon ;

When to throw an exception in try with resources statement?

In the above example, exceptions can be thrown from the try-with-resources statement when: The file test.txt is not found. Closing the BufferedReader object. An exception can also be thrown from the try block as a file read can fail for many reasons at any time.

What is try with resources in C++?

A resource is an object to be closed at the end of the program. As seen from the above syntax, we declare the try-with-resources statement by, declaring and instantiating the resource within the try clause. specifying and handling all exceptions that might be thrown while closing the resource.


2 Answers

There is technically a path for which the BufferedReader would not be closed: if reader.close() would throw an exception, because you catch the exception and do nothing with it. This can be verified by adding reader.close() again in your catch block:

    } finally
    {
        try {
            reader.close();
        } catch (Exception e) {
            reader.close();
        }
    }

Or by removing the try/catch in the finally:

    } finally
    {
            reader.close();
    }

This will make the warnings disappear.

Of course, it doesn't help you. If reader.close() is failing, then calling it again does not make sense. The thing is, the compiler is not smart enough to handle this. So the only sensible thing you can do is to add a @SuppressWarnings("resource") to the method.

Edit If you are using Java 7, what you can/should do is using try-with-resources functionality. This will get the warnings right, and makes you code simpler, saving you a finally block:

public boolean isValid(File file) throws IOException
{
  try(BufferedReader reader = new BufferedReader(new FileReader(file)))
  {
    String line;
    while ((line = reader.readLine()) != null)
    {
      line = line.trim();
      if (line.isEmpty())
        continue;
      if (line.startsWith("#") == false)
        return false;
      if (line.startsWith("#MLProperties"))
        return true;
    }
  } 
  return false;
}
like image 122
Cyrille Ka Avatar answered Sep 21 '22 16:09

Cyrille Ka


If the BufferedReader constructor throws an exception (e.g. out of memory), you will have FileReader leaked.

like image 40
kan Avatar answered Sep 22 '22 16:09

kan