一个非常简单的for循环,我用它作为伪循环来保持我的机器繁忙,阻塞了JVM的所有进程。即使在最简单的星座中,失速也持续存在。
下面是两个for循环的例子(第一个阻塞,第二个不是),唯一的区别是迭代器"i"的类型,即int与long:
public class Main {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTimerHandler(), new Date(), 1000);
float b = 1;
// after a few seconds this loop uses 200% CPU and blocks the timer
for (int i=0; i<2000000000; i++) {
b += i/3.141592;
}
System.out.println("Result: " + b);
b = 1;
// this version uses 100% CPU throughout the entire loop and doesn't block the timer
for (long i=0; i<2000000000L; i++) {
b += i/3.141592;
}
System.out.println("Result: " + b);
timer.cancel();
}
}
// helps to show whether the JVM is stalled or not
class MyTimerHandler extends TimerTask {
@Override
public void run() {
System.out.println("timestamp=" + new Date().toString());
}
}
我们在两个不同的机器/jvm上重现了这个问题:
- Arch Linux 3.7.7-1-Arch…x86_64 GNU/Linuxjava版本"1.7.0_09",java(TM)SE运行时环境(内部版本1.7.0_09-b05)
- OSX 10.8.2…x86_64java版本"1.7.0_07",java(TM)SE运行时环境(内部版本1.7.0_07-b10)
更新&澄清:
- 问题是,为什么和到底发生了什么,而不是"解决"给定的例子,即为什么第一个for循环的行为如此奇怪,使用了2倍多的CPU并阻塞了JVM的所有线程
- 示例代码终止并给出正确的结果
- 计时器只是用来演示的,不管有没有它,问题都会发生
- int的上限远高于2000000000
- 到目前为止,这个问题影响到所有经过测试的JVM
- JProfiler和其他调试工具在第一个for循环期间也会被中断/停滞
这是因为编译器进行了优化(可能是试图展开循环)。它在一个单独的线程上运行,这就是为什么您可以看到200%的CPU利用率。如果您从第一个循环中创建一个方法,并在第二次运行两次,它将按预期工作。
试着这样运行JVM:
java -Xint Main
该选项将禁用HotSpot编译器。在我的情况下,计时器线程每秒钟都会打印一次,没有停顿
如果使用java -XX:+PrintCompilation Main
运行,您将看到编译器在第一个循环的中间打印"made not entrat"。
79 6 java.lang.String::lastIndexOf (52 bytes)
90 1 % test.Main::main @ 33 (141 bytes)
timestamp=Thu Feb 14 12:10:40 PST 2013
timestamp=Thu Feb 14 12:10:41 PST 2013
timestamp=Thu Feb 14 12:10:42 PST 2013
timestamp=Thu Feb 14 12:10:43 PST 2013
timestamp=Thu Feb 14 12:10:44 PST 2013
13202 1 % test.Main::main @ -2 (141 bytes) made not entrant
timestamp=Thu Feb 14 12:10:53 PST 2013
Result: 1.80143985E16
13202 2 % test.Main::main @ 85 (141 bytes)
timestamp=Thu Feb 14 12:10:54 PST 2013
timestamp=Thu Feb 14 12:10:55 PST 2013
交换循环,它会在两个循环之间打印"made not entrat"。
72 6 java.lang.String::lastIndexOf (52 bytes)
85 1 % test.Main::main @ 33 (141 bytes)
timestamp=Thu Feb 14 12:12:38 PST 2013
timestamp=Thu Feb 14 12:12:39 PST 2013
timestamp=Thu Feb 14 12:12:40 PST 2013
timestamp=Thu Feb 14 12:12:41 PST 2013
15415 1 % test.Main::main @ -2 (141 bytes) made not entrant
Result: 1.80143985E16
15415 2 % test.Main::main @ 88 (141 bytes)
timestamp=Thu Feb 14 12:12:42 PST 2013
timestamp=Thu Feb 14 12:12:43 PST 2013
使用AProVE(http://aprove.informatik.rwth-aachen.de)我证明了第一个循环确实正在终止。请仔细查看其他可能不终止的代码,很可能是计时器(如注释所示)。