MaxJavaStackTraceDepth是干嘛的?

电脑杂谈  发布时间:2020-02-18 10:12:02  来源:网络整理

java stack trace_stack trace 什么意思_print stack trace

Jack是帮Android用的、用Java写的一个把Java源码编译到DEX格式的编译器。而这个编译器自身一般是在Oracle JDK / OpenJDK的HotSpot VM上运行的。这里涉及的参数就是一个HotSpot VM特有的参数。

题主所说的代码是从这个changeset放出来的:

android.googlesource.com/toolchain/jack/+/97458db2738a00b829461967838c2567938c14%5E%21/server/jack-server/etc/jack-admin

android-review.googlesource.com/#/c/203451/

从commit message和review thread可以发现java stack tracejava stack trace,作者加入 -XX:MaxJavaStackTraceDepth=-1 参数的目的是使JVM在推动 java.lang.Throwable.fillInStackTrace() 时把整个调用栈上所有Java栈帧的信息都记录下来。

java stack trace_print stack trace_stack trace 什么意思

HotSpot VM的 -XX:MaxJavaStackTraceDepth 参数的默认值是1024,也就是说 fillInStackTrace() 在记录至最多1024层调用栈帧信息后才会停止爬栈。这是因为爬栈并将栈帧信息转化为Java对象(StackTraceElement)是个非常慢的动作,限制一下最大深度可以把 fillInStackTrace() 的最大开销给限制住。

  product(intx, MaxJavaStackTraceDepth, 1024,                               \
          "The maximum number of lines in the stack trace for Java "        \
          "exceptions (0 means all)")                                       \

我们可以用一个小实验来演示一下:

public class StackTraceDepthDemo {
  public static void demo(int depth) {
    if (depth > 0) demo(depth - 1);
    else throw new RuntimeException("Count the stack trace depth");
  }
  public static void main(String[] args) {
    demo(2047);
  }
}

这个实例运行的之后,如果 fillInStackTrace() 能收集整个调用栈上的所有Java栈帧的信息,那么我们必须能看到2049层栈帧(2048层 demo() + 1层 main()),其中更后面的一行必须是 at StackTraceDepthDemo.main() 。

java stack trace_stack trace 什么意思_print stack trace

在Mac OS X上用Oracle JDK8u101以默认参数来运行:

$ java StackTraceDepthDemo 2>&1 | grep 'at StackTraceDepthDemo' | wc -l
    1024

看,只爬到了1024层栈帧。如果我们根据题主引用的代码去配置参数:

$ java -XX:MaxJavaStackTraceDepth=-1 StackTraceDepthDemo 2>&1 | grep 'at StackTraceDepthDemo' | wc -l
    2049
$ java -XX:MaxJavaStackTraceDepth=-1 StackTraceDepthDemo 2>&1 | tail -n1
	at StackTraceDepthDemo.main(StackTraceDepthDemo.java:8)

这样就爬到了整个调用栈的所有Java栈帧了。

java stack trace_stack trace 什么意思_print stack trace

原理:

其实这算是HotSpot VM的一个bug,不过是bug被当成feature来用了。

前面看到了 MaxJavaStackTraceDepth 参数的声明,它被声明为 intx 类型,这也许就是 intptr_t ,所以是带符号的、指针长度的整型。所以它其实可以被设定为负数。

$ java -XX:+PrintFlagsFinal -XX:MaxJavaStackTraceDepth=-1 -version | grep MaxJavaStackTraceDepth
     intx MaxJavaStackTraceDepth                   := -1                                  {product}
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)

但根据这个参数本身的设计动机,它必须只容许非负数的值,所以虽然它的声明必须用 uintx (就是uintptr_t)而不需要用 intx 。这就是bug的源头所在。

print stack trace_java stack trace_stack trace 什么意思

HotSpot VM里, java.lang.Throwable.fillInStackTrace() 的native实现有如下的结构:

void java_lang_Throwable::fill_in_stack_trace(Handle throwable, methodHandle method, TRAPS) {
  if (!StackTraceInThrowable) return;
  
  // ...

  int max_depth = MaxJavaStackTraceDepth;
  JavaThread* thread = (JavaThread*)THREAD;
  BacktraceBuilder bt(CHECK);
  // ...
  int total_count = 0;
  for (frame fr = thread->last_frame(); max_depth != total_count;) {
    // ...
    bt.push(method, bci, CHECK);
    total_count++;
  }
  // ...
}

可以发现,这里会循环遍历整个调用栈上的所有栈帧,直到 max_depth == total_count 。然而 total_count 是从0开始向上数的,除非已经到整数溢出,不然它仍然递增上去的话是无论如何也不会跟配置为 -1 的 max_depth 相等,所以就超过了“不设限制,把整个调用栈的所有栈帧都爬了”的目的。

然而如果HotSpot VM把这个bug给修了,把 MaxJavaStackTraceDepth 的声明改为 uintx 类型的话,配置为 -1 就没用了——HotSpot VM在处理命令行参数的之后即使要对 uintx 类型的参数配置负值的话,这个配置的值会直接被忽略。

如果非应爬整个栈,比较好的做法是把 MaxJavaStackTraceDepth 配置为一个很大的正整数,例如说 INT32_MAX ,通常也就够用了。


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/shouji/article-141189-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...