3

一个非常简单的 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/Linux java 版本“1.7.0_09”,Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
  • OSX 10.8.2 ... x86_64 java 版本“1.7.0_07”,Java(TM) SE 运行时环境(内部版本 1.7.0_07-b10)

更新和澄清:

  • 问题是,为什么以及究竟发生了什么,而不是“解决”给定的示例,即为什么第一个 for 循环表现得如此怪异,使用 2 倍以上的 CPU 并阻塞了 JVM 的所有线程。
  • 示例代码终止并给出正确的结果。
  • 计时器仅用于演示,有或没有它都会出现问题。
  • int 的限制远高于 2,000,000,000。
  • 到目前为止,该问题影响了所有经过测试的 JVM。
  • JProfiler 和其他调试工具在第一个 for 循环期间也会中断/停止。
4

2 回答 2

1

这是因为优化编译器(可能试图展开循环)。它在单独的线程上运行,这就是为什么您会看到 200% 的 CPU 利用率。如果您在第一个循环之外创建一个方法并第二次运行它两次,它会按预期工作。

尝试像这样运行你的 JVM:

java -Xint Main

该选项禁用 HotSpot 编译器。在我的情况下,计时器线程每秒打印一次,没有暂停。


如果你运行,java -XX:+PrintCompilation Main你会看到编译器在第一个循环的中间打印“made not entrant”。

    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 entrant”。

    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
于 2013-02-14T18:32:38.370 回答
0

使用 AProVE ( http://aprove.informatik.rwth-aachen.de ) 我证明了第一个循环确实正在终止。请仔细查看其他可能的非终止代码,很可能是计时器(如注释中所示)。

于 2013-02-14T19:05:35.620 回答