繁体   English   中英

在finally块中抛出异常会导致性能问题吗?

[英]Is throwing an exception inside a finally block a performance issue?

在Rational Application Developer(基于eclipse的RAD)中,在软件分析器下,我看到了一个代码审查注释(在Performance => Memory部分下),说“避免最终的throw语句”。

如何在finally块中定义throw会影响性能?

在此输入图像描述

这是代码片段,我们已经建议更改代码以记录异常跟踪并且不抛出异常,

     } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (final IOException ex) {
                throw ex;
            }
        }
    }

我只是想知道这会如何影响内存和性能?

finally块抛出的异常将替换从try中抛出的任何异常,并且有关真正问题的信息可能会丢失。

由于在这种情况下允许try-finally块抛出IOException ,这是一个更好的编写方式:

try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get("file.txt"))) {
  /* Work with `bufferedReader` */
}

这会在块退出时自动关闭阅读器,并且很好地处理任何结果异常,即使try块内的代码首先使用Throwable的“suppress”机制抛出自己的异常。

如果try块无异常地完成,则结果将是关闭资源的结果(异常与否)。 如果try块抛出异常,那将是异常结果。 但是如果close()方法也引发异常,它将作为“抑制”异常添加到try块的异常中。 您可以以编程方式查询它,并且在打印堆栈跟踪时,将显示被抑制的异常,就像您可能更熟悉的“由...引起的”异常一样。

并且,您可以尝试使用多种资源; 这些都将被关闭,并且可以抑制多个闭包异常。

这假设您正在使用文件I / O,但相同的“try-with-resources”结构将适用于实现AutoCloseable (流,SQL对象等)的任何内容。

这不是性能问题。 这是一个正确性问题。 (Marko Topolnik评论警告错误分类似乎对我来说是正确的,我能看到性能角度的唯一方法是,如果try块中抛出的异常被掩盖,那么创建它及其堆栈跟踪所花费的精力就会被浪费掉。但这距离最大的问题还有很长的路要走。)

不在finally块中抛出异常的两个原因:

  • 让finally块抛出异常可以屏蔽try块中抛出的任何异常,这样就会丢失原始的,有用的异常,在日志中不会留下实际出错的线索。

  • 如果正常的控制流因为关闭时抛出的异常而中断,您可能会让一些短暂的I / O故障(您没有任何控制权,哪些不影响您的业务逻辑)会阻止某些有用的操作从完成工作(例如,它可能导致当前事务回滚)。 这可能取决于所涉及的资源类型; 也许有些情况下你真的可能想要失败整个事情,如果关闭没有干净地发生,但对于很多常见的情况(如JDBC),没有充分的理由去关心。

使用try-with-resources成功排除了异常屏蔽的可能性。 但是,如果try逻辑在没有异常的情况下完成,它会让抛出的任何东西得到传播。 由于它是一种语言添加,Oracle必须采取最保守的方法,只需要知道它在使用它时正在做什么。

理想解决方案

try (BufferedReader bufferedReader = ...) {
  //do stuff
}

但也许你在java 1.6中:

BufferedReader bufferedReader = null;
try{
  bufferedReader = ...;
  //do stuff
} finally {
  if (bufferedReader != null) {
    try {
      bufferedReader.close();
    } catch (final IOException ex) {
      logger.error("Problem while cleaning up.", ex);
    }
  }
}

在finally块中,您可以调用另一个传递对象B的方法来进行清理。 你可以尝试在该方法中捕获finally块。

如果你有单独的每个对象A和B的清理方法,你可以处理try catch finally块,这很好。 您可以调用两种方法来清理对象。 每个对象清理的异常都是独立处理的。

在给出的代码片段中,没有指向throw语句。 bufferedReader.close()方法已抛出异常。 catch块应该只处理它,而不是抛出另一个异常。 实际上,虽然捕获抛出的异常在finally块中肯定是有效的,但我真的不能想到在finally块中抛出异常的正当理由。

至于性能下降,从轶事的角度来看,重新抛出一个被捕获的异常显然不如仅仅处理它有效。

至于一个特定的例子,这将是有害的,这样的事情让我想起了我的头脑。 如果你有另一个方法在finally块中执行清理,例如FileOutputStream.close(),并且第一个方法抛出错误,则第二个方法永远不会运行,因为throw语句结束了块的处理。 那时你会泄漏资源。

总而言之,try / catch在finally块中很好,但是为了效率和意外后果(内存泄漏)应该避免使用throw语句。

最后用于抛出/捕获异常之后。 因此,通过设计,最终不需要抛出异常。 这应该只在你的try块中完成。

可能有助于您了解有关Java中try-catch-finally异常处理的更多信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM