简单for循环中的进程停滞,取决于迭代器类型(潜在的JVM错误?)



一个非常简单的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)我证明了第一个循环确实正在终止。请仔细查看其他可能不终止的代码,很可能是计时器(如注释所示)。

最新更新