当JVM无法达到安全点时如何获取Java堆栈

时间:2013-11-22 00:34:55

标签: garbage-collection jvm stack-trace freeze jit

我们最近有一种情况,我们的一个生产JVM会随机冻结。 Java进程正在烧制CPU,但所有可见活动都将停止:没有日志输出,没有写入GC日志,没有响应任何网络请求等等。此过程将一直持续到此状态,直到重新启动。

事实证明,当在某些输入上调用时,org.mozilla.javascript.DToA类会混淆,并使用巨大的值(例如5 ^ 2147483647)调用BigInteger.pow,这会触发JVM冻结。我的猜测是,一些大循环,可能是在java.math.BigInteger.multiplyToLen中,在循环中没有安全点检查的情况下进行了JIT。下次JVM需要暂停进行垃圾回收时,它会冻结,因为运行BigInteger代码的线程很长时间都不会达到安全点。

我的问题:将来,我该如何诊断这样的安全点问题?杀-3没有产生任何输出;我认为它依赖于安全点来生成准确的堆栈。是否有任何生产安全工具可以从正在运行的JVM中提取堆栈而无需等待安全点? (在这种情况下,我很幸运,并且在调用BigInteger.pow之后设法抓住了一组堆栈跟踪,但是在它运行到足够大的输入以完全楔入JVM之前。没有那个运气,我我不确定我们怎么会诊断出这个问题。)

修改:以下代码说明了问题。

// Spawn a background thread to compute an enormous number.
new Thread(){ @Override public void run() {
  try {
    Thread.sleep(5000);
  } catch (InterruptedException ex) {
  }
  BigInteger.valueOf(5).pow(100000000);
}}.start();

// Loop, allocating memory and periodically logging progress, so illustrate GC pause times.
byte[] b;
for (int outer = 0; ; outer++) {
  long startMs = System.currentTimeMillis();
  for (int inner = 0; inner < 100000; inner++) {
    b = new byte[1000];
  }

  System.out.println("Iteration " + outer + " took " + (System.currentTimeMillis() - startMs) + " ms");
}

这将启动一个等待5秒的后台线程,然后启动一个巨大的BigInteger计算。在前台,它然后重复分配一系列100,000个1K块,记录每个100MB系列的经过时间。在5秒钟期间,每个100MB系列在我的MacBook Pro上运行大约20毫秒。一旦BigInteger计算开始,我们开始看到交错的长暂停。在一次测试中,暂停依次是175ms,997ms,2927ms,4222ms和22617ms(此时我中止了测试)。这与BigInteger.pow()调用一系列更大的乘法运算是一致的,每次运算都需要更长时间才能达到安全点。

3 个答案:

答案 0 :(得分:10)

你的问题非常感兴趣。你对JIT是正确的。首先我尝试使用GC类型,但这没有任何效果。然后我试图禁用JIT,一切正常:

java -Djava.compiler=NONE Tests

然后打印出JIT汇编:

java -XX:+PrintCompilation Tests

并注意到问题在BigInteger类中的一些编译之后开始,我试图从编译中逐个排除方法,最后找到原因:

java -XX:CompileCommand=exclude,java/math/BigInteger,multiplyToLen -XX:+PrintCompilation Tests

对于大型数组,此方法可能会运行很长时间,问题可能确实存在于安全点。由于某种原因,它们没有插入,但应该在编译代码中。看起来像个bug。下一步应该是analyze assembly code,我还没有这样做。

答案 1 :(得分:2)

这不是错误,而是性能功能。 JVM从计数循环中消除了安全点检查,从而使它们运行得更快。希望可以

  • 您关心STW暂停并且没有多余的循环
  • 或者您有更长的循环,但是可以使用安全点作为最终目标

如果不适合您,则可以使用以下标志将其关闭:-XX:+UseCountedLoopSafepoints

回答标题问题后,您仍然可以使用gdb停止并探索程序,但是堆栈跟踪并不是很好。

答案 2 :(得分:1)

或许这就是&#34; -F&#34; jstack的选项适用于:

OPTIONS
  -F
     Force a stack dump when 'jstack [-l] pid' does not respond.

我总是想知道何时可以提供帮助。