可能重复:
Java 异常有多慢?
以下两个程序的运行时间大致相同:
public class Break {
public static void main(String[] argv){
long r = 0, s = 0, t = 0;
for(long x = 10000000; x > 0; x--){
long y = x;
while(y != 1){
if(y == 0)
throw new AssertionError();
try2: {
try1: {
for(;;){
r++;
if(y%2 == 0)
break try1;
y = y*3 + 1;
}
}/*catch(Thr _1)*/{
for(;;){
s++;
if(y%2 == 1)
break try2;
y = y/2;
}
}
}/*catch(Thr _2)*/{
t++;
}
}
}
System.out.println(r + ", " + s + ", " + t);
}
}
public class Try {
private static class Thr extends Throwable {}
private static final Thr thrown = new Thr();
public static void main(String[] argv){
long r = 0, s = 0, t = 0;
for(long x = 10000000; x > 0; x--){
long y = x;
while(y != 1){
try{
if(y == 0)
throw new AssertionError();
try{
for(;;){
r++;
if(y%2 == 0)
throw thrown;
y = y*3 + 1;
}
}catch(Thr _1){
for(;;){
s++;
if(y%2 == 1)
throw thrown;
y = y/2;
}
}
}catch(Thr _2){
t++;
}
}
}
System.out.println(r + ", " + s + ", " + t);
}
}
$ for x in Break Try; 回声 $x; 时间 java $x; 完毕 休息 1035892632、1557724831、520446316 真正的 0m10.733s 用户 0m10.719s 系统 0m0.016s 尝试 1035892632、1557724831、520446316 真正的 0m11.218s 用户 0m11.204s 系统 0m0.017s
但是接下来的两个程序所花费的时间是相对不同的:
public class Return {
private static int tc = 0;
public static long find(long value, long target, int depth){
if(depth > 100)
return -1;
if(value%100 == target%100){
tc++;
return depth;
}
long r = find(target, value*29 + 4221673238171300827l, depth + 1);
return r != -1? r : find(target, value*27 + 4494772161415826936l, depth + 1);
}
public static void main(String[] argv){
long s = 0;
for(int x = 0; x < 1000000; x++){
long r = find(0, x, 0);
if(r != -1)
s += r;
}
System.out.println(s + ", " + tc);
}
}
public class Throw {
public static class Found extends Throwable {
// note the static!
public static int value = 0;
}
private static final Found found = new Found();
private static int tc = 0;
public static void find(long value, long target, int depth) throws Found {
if(depth > 100)
return;
if(value%100 == target%100){
Found.value = depth;
tc++;
throw found;
}
find(target, value*29 + 4221673238171300827l, depth + 1);
find(target, value*27 + 4494772161415826936l, depth + 1);
}
public static void main(String[] argv){
long s = 0;
for(int x = 0; x < 1000000; x++)
try{
find(0, x, 0);
}catch(Found _){
s += found.value;
}
System.out.println(s + ", " + tc);
}
}
$ for x in Return Throw; 回声 $x; 时间 java $x; 完毕 返回 84227391, 1000000 实际0m2.437s 用户 0m2.429s 系统 0m0.017s 扔 84227391, 1000000 真正的 0m9.251s 用户 0m9.215s 系统 0m0.014s
我想一个简单的 try/throw/catch 机制看起来有点像一个至少部分尾调用优化的返回(所以它直接知道控制应该返回到哪里(最近的捕获)),但是,当然,JRE 实现做了很多优化。
为什么后者差别很大,而前者差别不大?是因为控制流分析确定前两个程序几乎相同,而实际的 try/throw/catch 特别慢,还是因为 Return 的find
展开到某种程度,从而避免了方法调用,而 Throw 的却不能, 或者 ..?谢谢。
编辑:这个问题对我来说似乎与Java 异常有多慢?因为它没有问为什么在与此类似的情况下会有如此大的差异。它还忽略了创建异常对象所花费的时间这一事实(除非fillInStackTrace
被覆盖,否则包括遍历堆栈并为其创建数组)。然而,它显然回答了我的部分问题:“是不是因为控制流分析确定前两个程序几乎相同”——尽管在答案中提到堆栈跟踪似乎很奇怪(这可能会使任何实际的 throw/catch 操作相形见绌——除非它进行一些复杂的分析来确定堆栈从未见过,这会使@Stephen 的回答变得奇怪)。