使用try ...最后资源泄漏?

时间:2013-03-06 16:39:48

标签: java eclipse resources eclipse-juno java-6

我在eclipse中正常工作,当我在此方法的return块内的try值中的资源泄漏警告被警告时:

@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;
}

我不明白它会如何导致资源泄漏,因为我在reader范围之外声明了try变量,在try块中添加了一个资源并将其关闭一个finally阻止使用其他try...catch来忽略异常,NullPointerException如果readernull出于某种原因...

据我所知,finally块总是在离开try...catch结构时执行,因此在try块内返回一个值仍然会执行finally块之前退出方法......

这可以通过以下方式轻松证明:

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);
}

结果是:

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

知道这一切,为什么eclipse告诉我Resource leak: 'reader' is not closed at this location我是否在finally区块中关闭它?


答案

我只想添加this answer他是正确的,如果new BufferedReader抛出异常,FileReader的实例将在垃圾收集器销毁时打开,因为它不会被分配对于任何变量,finally块都不会关闭它,因为reader将为null

这是我修复这种可能泄漏的方法:

@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;
}

4 个答案:

答案 0 :(得分:4)

技术上有一个BufferedReader不会被关闭的路径:如果reader.close()会抛出异常,因为你捕获了异常而对它没有任何作用。这可以通过在catch块中再次添加reader.close()来验证:

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

或者删除finally中的try / catch:

    } finally
    {
            reader.close();
    }

这会使警告消失。

当然,它对你没有帮助。如果reader.close()失败,那么再次调用它是没有意义的。问题是,编译器不够智能来处理这个问题。因此,您唯一明智的做法是在方法中添加@SuppressWarnings("resource")

编辑如果您使用的是Java 7,您可以/应该做的是使用try-with-resources功能。这将使警告正确,并使您的代码更简单,为您节省finally块:

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;
}

答案 1 :(得分:2)

如果BufferedReader构造函数抛出异常(例如内存不足),则会FileReader泄露。

答案 2 :(得分:1)

//If this line throws an exception, then neither the try block
    //nor the finally block will execute.
    //That is a good thing, since reader would be null.
    BufferedReader reader = new BufferedReader(new FileReader(aFileName));
    try {
      //Any exception in the try block will cause the finally block to execute
      String line = null;
      while ( (line = reader.readLine()) != null ) {
        //process the line...
      }
    }
    finally {
      //The reader object will never be null here.
      //This finally is only entered after the try block is 
      //entered. But, it's NOT POSSIBLE to enter the try block 
      //with a null reader object.
      reader.close();
    }

答案 3 :(得分:1)

由于close()可以抛出异常(为什么哦,为什么他们这样设计呢......)我倾向于使用双重尝试

try {
  BufferedReader reader = new BufferedReader(new FileReader(file));
  try {
    // do stuff with reader
  } finally {
    reader.close();
  }
} catch(IOException e) {
  // handle exceptions
}

由于这个习语消除了finally块中的try / catch,因此可能足以让Eclipse满意。

new BufferedReader(...)本身不能抛出IOException,但从技术上讲,如果FileReader构造函数抛出BufferedReader或{{1},这仍然会泄漏RuntimeException }}