Try-Catch 比 Try-With-Resources 更贵还是更便宜
Is Try-Catch More or Less Expensive Than Try-With-Resources
问题
我最近才开始重新使用 Java,但从未有机会使用 try-with-resources
。表面上它看起来很棒,因为它可以减少代码,但在幕后,它比传统的 try-catch
更昂贵还是更便宜?我知道 try-catch
已经是一项昂贵的操作,因此我很好奇。
我对这两种类型进行了简单测试,并没有发现太大的区别:
测试示例
尝试资源测试
long startTime = System.currentTimeMillis();
ArrayList<String> list = null;
try (Scanner sc = new Scanner(new File("file.txt"))) {
list = new ArrayList();
while (sc.hasNext()) {
list.add(sc.next());
}
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
} finally {
long endTime = System.currentTimeMillis();
System.out.println("The program completed in " + (endTime - startTime) + " ms");
}
传统 Try-Catch 测试
long startTime = System.currentTimeMillis();
ArrayList<String> list = null;
Scanner sc = null;
try {
sc = new Scanner(new File("file.txt"));
list = new ArrayList();
while (sc.hasNext()) {
list.add(sc.next());
}
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
} finally {
sc.close();
long endTime = System.currentTimeMillis();
System.out.println("The program completed in " + (endTime - startTime) + " ms");
}
结果
两者都导致了 15-16 毫秒的时间 - 根本没有真正明显的区别。但不可否认,这是一个非常小的测试示例。
我的问题又来了:引擎盖下 try-with-resources
比传统 try-catch
贵还是便宜?
- try-catch 不是昂贵的部分。 抛出 异常是(生成堆栈跟踪)。
上面的- "Expensive"表示"costs some microseconds"。
- try-with-resources 只是 try-catch,需要正确的代码才能可靠地关闭资源。
- 由于尝试在 HotSpot 等优化运行时中测量性能的所有众所周知的陷阱,您的测量代码无法证明任何事情。你需要热身,重复同样的动作很多次等等。
- 如果您的结果超过 10 毫秒,那么显然您不会遇到 try-catch 的问题,这总共会造成几微秒的开销。
Try-catch-finally 和 try-with-resources 具有基本相同的性能,因为在幕后它们生成基本相同的字节码。
但是,您的第二个版本(try..catch..finally)的表述并不完全正确,因为它可能(理论上)在调用 sc.close()
时导致不希望的 NullPointerException
。如果构造 Scanner
的行为导致抛出异常,则 sc
将不会被赋值,并且将是 null
.
您应该在 try..finally
之外构建 Scanner 并将其更改为:
Scanner sc = null;
try {
sc = new Scanner(new File("file.txt"));
...
至:
Scanner sc = new Scanner(new File("file.txt"));
try {
...
或者,您应该在调用 sc.close()
之前检查 finally
子句中的 sc != null
。如果您在 try..finally 之外创建扫描器,则没有必要这样做,所以我建议您这样做。
要完成与 try-with-resources 相同的工作,您还需要在 sc.close()
周围放置第二个 try..catch
和一个空的 catch 块,以忽略关闭期间抛出的任何异常.如果你这样做了,我想你就不必太担心 null 检查了。
是苹果和橘子。 ARM(自动资源管理,或 try-with-resources)块比您展示的老式 try-catch-finally 块做的更多。那是因为它生成代码来处理资源关闭中抛出的异常,suppression mechanism. (A related answer 对此进行了一些详细讨论。)
如果您正在编写新代码,请使用 ARM 块。它更易于阅读、维护,而且功能更多。除非您 运行 处于严格受限的环境(如智能卡)中,否则这些优势可能会超过一些额外字节代码的成本。
传统的try-catch:在try块中,你的代码可能会出现异常,这个异常会被抛出,然后被catch块捕获。然后 catch 块将处理异常。如果你在 try
之前打开一个源,比如文件或其他东西,你需要在 finally
中关闭它们,那么 close()
也可能在 finally
中执行抛出一个异常,这将替换 try
中抛出的异常 这种情况在 SE 7 之前通过嵌套的 try-catch 解决,就像这样
try
{
try
{
code with exception;
}
finally
{
close();
}
}
catch(Exception e)
{
Deal the exception
}
这个真的很复杂,所以在SE 7之后我们使用try-with-resource来解决这个问题,当你完成try
块时,try(Here)
中的资源将全部关闭。
参考 "Core Java" Edition:9 基于 SE 7
问题
我最近才开始重新使用 Java,但从未有机会使用 try-with-resources
。表面上它看起来很棒,因为它可以减少代码,但在幕后,它比传统的 try-catch
更昂贵还是更便宜?我知道 try-catch
已经是一项昂贵的操作,因此我很好奇。
我对这两种类型进行了简单测试,并没有发现太大的区别:
测试示例
尝试资源测试
long startTime = System.currentTimeMillis();
ArrayList<String> list = null;
try (Scanner sc = new Scanner(new File("file.txt"))) {
list = new ArrayList();
while (sc.hasNext()) {
list.add(sc.next());
}
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
} finally {
long endTime = System.currentTimeMillis();
System.out.println("The program completed in " + (endTime - startTime) + " ms");
}
传统 Try-Catch 测试
long startTime = System.currentTimeMillis();
ArrayList<String> list = null;
Scanner sc = null;
try {
sc = new Scanner(new File("file.txt"));
list = new ArrayList();
while (sc.hasNext()) {
list.add(sc.next());
}
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
} finally {
sc.close();
long endTime = System.currentTimeMillis();
System.out.println("The program completed in " + (endTime - startTime) + " ms");
}
结果
两者都导致了 15-16 毫秒的时间 - 根本没有真正明显的区别。但不可否认,这是一个非常小的测试示例。
我的问题又来了:引擎盖下 try-with-resources
比传统 try-catch
贵还是便宜?
- try-catch 不是昂贵的部分。 抛出 异常是(生成堆栈跟踪)。 上面的
- "Expensive"表示"costs some microseconds"。
- try-with-resources 只是 try-catch,需要正确的代码才能可靠地关闭资源。
- 由于尝试在 HotSpot 等优化运行时中测量性能的所有众所周知的陷阱,您的测量代码无法证明任何事情。你需要热身,重复同样的动作很多次等等。
- 如果您的结果超过 10 毫秒,那么显然您不会遇到 try-catch 的问题,这总共会造成几微秒的开销。
Try-catch-finally 和 try-with-resources 具有基本相同的性能,因为在幕后它们生成基本相同的字节码。
但是,您的第二个版本(try..catch..finally)的表述并不完全正确,因为它可能(理论上)在调用 sc.close()
时导致不希望的 NullPointerException
。如果构造 Scanner
的行为导致抛出异常,则 sc
将不会被赋值,并且将是 null
.
您应该在 try..finally
之外构建 Scanner 并将其更改为:
Scanner sc = null;
try {
sc = new Scanner(new File("file.txt"));
...
至:
Scanner sc = new Scanner(new File("file.txt"));
try {
...
或者,您应该在调用 sc.close()
之前检查 finally
子句中的 sc != null
。如果您在 try..finally 之外创建扫描器,则没有必要这样做,所以我建议您这样做。
要完成与 try-with-resources 相同的工作,您还需要在 sc.close()
周围放置第二个 try..catch
和一个空的 catch 块,以忽略关闭期间抛出的任何异常.如果你这样做了,我想你就不必太担心 null 检查了。
是苹果和橘子。 ARM(自动资源管理,或 try-with-resources)块比您展示的老式 try-catch-finally 块做的更多。那是因为它生成代码来处理资源关闭中抛出的异常,suppression mechanism. (A related answer 对此进行了一些详细讨论。)
如果您正在编写新代码,请使用 ARM 块。它更易于阅读、维护,而且功能更多。除非您 运行 处于严格受限的环境(如智能卡)中,否则这些优势可能会超过一些额外字节代码的成本。
传统的try-catch:在try块中,你的代码可能会出现异常,这个异常会被抛出,然后被catch块捕获。然后 catch 块将处理异常。如果你在 try
之前打开一个源,比如文件或其他东西,你需要在 finally
中关闭它们,那么 close()
也可能在 finally
中执行抛出一个异常,这将替换 try
中抛出的异常 这种情况在 SE 7 之前通过嵌套的 try-catch 解决,就像这样
try
{
try
{
code with exception;
}
finally
{
close();
}
}
catch(Exception e)
{
Deal the exception
}
这个真的很复杂,所以在SE 7之后我们使用try-with-resource来解决这个问题,当你完成try
块时,try(Here)
中的资源将全部关闭。
参考 "Core Java" Edition:9 基于 SE 7