我知道在使用方面存在差异。但我想讨论性能差异。
顺便说一下,“Effective Java”中有一句话:
将代码放在try-catch块中会禁止现有JVM实现可能执行的某些优化。
因此,如果有一个抛出异常的方法,是否意味着这种优化不会应用于此方法?
从论文中,Optimizing Java Programs in the Presence of Exceptions:
Java语言规范要求异常是精确的,这意味着:
- 抛出异常时,相应异常处理程序入口处的程序状态observable必须与原始程序中的相同;和
- 必须按照原始(未优化)程序指定的顺序抛出异常。
为了满足精确的异常要求,Java编译器禁用了可能引发异常的指令的许多重要优化......这妨碍了各种程序优化,例如指令调度,指令选择,循环转换和并行化。
在存在try-catch
块的情况下,JVM维护一个异常表。对于每个新的catch块,编译器将向异常表添加一个条目。如果发生异常,JVM需要遍历表中的每个条目并检查是否在异常表中定义了异常(这也需要进行类型分析)。结果是一个更复杂的控制结构,这反过来将极大地妨碍编译器优化。
另一方面,throws
允许异常传播。虽然这可能对性能更好,但未捕获的异常可能会使线程,整个应用程序崩溃,甚至杀死JVM本身!
如何决定何时使用try-catch
?
我认为正确性和可靠性问题比这里的表现更重要。但是如果你只考虑了性能,那么this link的作者已经在存在异常的情况下对java代码进行了广泛的基准测试。他们最基本的结论是,如果你使用异常来处理真正特殊情况(不是作为程序流控制的手段),那么性能就不会受到太大影响。
为什么不尝试代码?
public class ExPerformanceTest {
private int someVar = 0;
private int iterations = 100000000;
@Test
public void throwTest() throws Exception {
long t;
t = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
throwingMethod();
}
t = System.currentTimeMillis() - t;
System.out.println("Throw Test took " + t + " ms");
}
@Test
public void tryCatchTest() throws Exception {
long t;
t = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
tryCatchMethod();
}
t = System.currentTimeMillis() - t;
System.out.println("Try-Catch Test took " + t + " ms");
}
@Test
public void anotherTryCatchTest() throws Exception {
long t;
t = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
tryCatchMethodThatNeverEverThrows();
}
t = System.currentTimeMillis() - t;
System.out.println("Try-Catch That Never Throws Test took " + t + " ms");
}
private void throwingMethod() throws Exception {
// do some stuff here
someVar++;
willNeverThrow();
}
private void tryCatchMethod() {
try {
// do some stuff here
someVar++;
willNeverThrow();
} catch (Exception e) {
System.out.println("You shouldn't see this ever");
}
}
private void tryCatchMethodThatNeverEverThrows() {
try {
// do some stuff here
someVar++;
} catch (Exception e) {
System.out.println("You shouldn't see this ever");
}
}
private void willNeverThrow() throws Exception {
if (someVar == -1) {
throw new RuntimeException("Shouldn't happen");
}
}
}
这给出了相当可观的数字:
运行1
Try-Catch That Never Throws Test took 36 ms
Throw Test took 139 ms
Try-Catch Test took 160 ms
跑2
Try-Catch That Never Throws Test took 26 ms
Throw Test took 109 ms
Try-Catch Test took 113 ms
跑3
Try-Catch That Never Throws Test took 32 ms
Throw Test took 137 ms
Try-Catch Test took 194 ms
显然,JVM发现tryCatchMethodThatNeverEverThrows
并没有真正需要一个catch
部分并对其进行了优化,因此方法执行所花费的时间比其他部分少了几倍。
在其他情况下,具有处理的catch
子句确实需要一些时间。
我用过这段代码
public class Test {
static int value;
public void throwsTest(int i) throws Exception {
value = ((value + i) / i) << 1;
// if ((i & 0xFFFFFFF) == 1000000000) {
// }
if ((i & 0x1) == 1) {
throw new Exception();
}
}
public void tryAndCatchTest(int i) {
value = ((value + i) / i) << 1;
try {
// if ((i & 0xFFFFFFF) == 1000000000) {
// }
if ((i & 0x1) == 1) {
throw new Exception();
}
} catch (Exception e) {
}
}
public static void main(String[] args) throws Exception {
int i;
long l;
Test t = new Test();
l = System.currentTimeMillis();
value = 0;
for (i = 1; i < 10000000; i++) {
try {
t.throwsTest(i);
} catch (Exception e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("Throws: " + l + " ms");
i = 0;
l = 0;
l = System.currentTimeMillis();
value = 0;
for (i = 1; i < 10000000; i++) {
try {
t.tryAndCatchTest(i);
} catch (Exception e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("Try and catch: " + l + " ms");
}
}
在测试之后,响应时间有时会更快,有时会尝试捕获。我尝试抛出错误并处理语句。因此,总之,它们都同样有效。
抛出异常的示例结果:抛出:6802 ms尝试并捕获:6586毫秒
示例结果没有:抛出:68毫秒尝试并捕获:72毫秒
我没有考虑在catch语句中使用代码运行,如果你使用try和catch语句,你需要运行该代码,你不会抛出异常。