JAVA:为什么以下无限循环终止而没有任何错误/异常

JAVA : Why does the following infinite loop terminate without any error / exception

本问题已经有最佳答案,请猛点这里访问。

我在处理Eclipse中的一些东西时,令我惊讶的是,我发现运行时的这段代码被终止,没有任何错误/异常

1
2
3
4
5
6
7
public class Test {
    public static void main(String[] args) {
        for(int i = 2; i > 0; i++){
            int c = 0;
        }
    }
}

当他的代码继续执行时

1
2
3
4
5
6
7
8
public class Test {
    public static void main(String[] args) {
        for(int i = 2; i > 0; i++){
            int c = 0;
            System.out.println(c);
        }
    }
}

即使两者都应该是无限循环,永远运行。对于第一个代码片段为何被终止,我是否遗漏了一些内容?


首先,这两个代码段都不是无限循环,因为当i通过Integer.MAX_VALUE时,它将变为负数。它们只需要很长时间就可以跑。第一个代码片段运行起来花费的时间要少得多,因为它不需要打印任何内容,而且编译器可能足够聪明,只需优化代码并消除循环,因为它什么也不做。

测试您的第一个代码片段,在循环前后添加System.out.println (System.currentTimeMillis ());,我得到:

1
2
1486539220248
1486539221124

也就是说,它在不到1秒的时间内运行。

稍微改变循环:

1
2
3
4
5
6
7
System.out.println (System.currentTimeMillis ());
for(int i = 2; i > 0; i++){
    int c = 0;
    if (i==Integer.MAX_VALUE)
        System.out.println (i);
}
System.out.println (System.currentTimeMillis ());

我得到了

1
2
3
1486539319309
2147483647
1486539319344

如您所见,从0Integer.MAX_VALUE的增量需要不到1秒的时间,然后溢出,此时循环终止。

添加到循环中的打印越多,终止所需的时间就越多。例如:

1
2
3
4
5
6
7
System.out.println (System.currentTimeMillis ());
for(int i = 2; i > 0; i++){
    int c = 0;
    if (i % 100000000 == 0)
        System.out.println (i);
}
System.out.println (System.currentTimeMillis ());

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1486539560318
100000000
200000000
300000000
400000000
500000000
600000000
700000000
800000000
900000000
1000000000
1100000000
1200000000
1300000000
1400000000
1500000000
1600000000
1700000000
1800000000
1900000000
2000000000
2100000000
1486539563232

现在花了3秒钟。


重点是:这个循环没有任何明显的副作用。

因此,可以假设编译器正在优化整个循环。另一方面,javac并不以进行大量优化而出名。因此:让我们看看会发生什么:

javap -c Test

...

public static void main(java.lang.String[]);

1
2
3
4
5
6
7
8
9
   0: iconst_2      
   1: istore_1      
   2: iload_1      
   3: ifle          14
   6: iconst_0      
   7: istore_2      
   8: iinc          1, 1
  11: goto          2
  14: return

显然:循环还在那里。所以真正的问题是:你的循环在某个时刻由于int溢出而停止;而你的程序的第一个版本只是更快地到达那一点(out.println()是一个非常昂贵的操作;与单纯的数字相加相比)