栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么在程序运行之间,导致递归方法的调用计数不同?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么在程序运行之间,导致递归方法的调用计数不同?

观察到的差异是由 后台JIT编译 引起的。

该过程如下所示:

  1. 方法
    f()
    开始在解释器中执行。
  2. 在多次调用(大约250次)之后,该方法被安排进行编译。
  3. 编译器线程与应用程序线程并行工作。同时,该方法在解释器中继续执行。
  4. 编译器线程完成编译后,方法入口点将被替换,因此下一个调用
    f()
    将调用该方法的已编译版本。

应用线程和JIT编译器线程之间基本上存在竞争。在方法的编译版本准备就绪之前,解释器可能会执行不同数量的调用。最后是解释帧和编译帧的混合。

难怪编译的框架布局与解释的框架布局不同。编译的帧通常较小;他们不需要将所有执行上下文存储在堆栈上(方法引用,常量池引用,事件探查器数据,所有参数,表达式变量等)。

此外,分层编译(JDK 8之后的默认设置)具有更多的竞赛可能性。可以有3种类型的帧组合:解释器C1和C2(请参见下文)。


让我们进行一些有趣的实验以支持该理论。

  1. 纯解释模式。没有JIT编译。
    没有比赛=>稳定的结果。

    $ java -Xint Main

    11895
    11895
    11895

  2. 禁用 后台 编译。JIT为ON,但与应用程序线程同步。
    再也没有比赛,但是由于已编译的帧,调用的数量现在更多了。

    $ java -XX:-BackgroundCompilation Main

    23462
    23462
    23462

  3. 执行 用C1编译所有内容。与以前的情况不同,堆栈上没有解释的帧,因此数量会更高。

    $ java -Xcomp -XX:TieredStopAtLevel=1 Main

    23720
    23720
    23720

  4. 现在, 执行 之前 用C2编译所有内容。这将以最小的帧生成最优化的代码。通话次数最多。

    $ java -Xcomp -XX:-TieredCompilation Main

    59300
    59300
    59300

由于默认堆栈大小为1M,因此这意味着该帧现在只有16个字节长。是吗?

    $ java -Xcomp -XX:-TieredCompilation -XX:CompileCommand=print,Main.f Main  0x00000000025ab460: mov    %eax,-0x6000(%rsp)    ; StackOverflow check  0x00000000025ab467: push   %rbp       ; frame link  0x00000000025ab468: sub    $0x10,%rsp   0x00000000025ab46c: movabs $0xd7726ef0,%r10      ; r10 = Main.class  0x00000000025ab476: addl   $0x2,0x68(%r10)       ; Main.counter += 2  0x00000000025ab47b: callq  0x00000000023c6620    ; invokestatic f()  0x00000000025ab480: add    $0x10,%rsp  0x00000000025ab484: pop    %rbp       ; pop frame  0x00000000025ab485: test   %eax,-0x23bb48b(%rip) ; safepoint poll  0x00000000025ab48b: retq

实际上,这里的帧是32个字节,但是JIT内联了一级递归。

  1. 最后,让我们看一下混合堆栈跟踪。为了获得它,我们将使JVM在StackOverflowError上崩溃(调试版本中可用的选项)。
    $ java -XX:AbortVMonException=java.lang.StackOverflowError Main

崩溃转储

hs_err_pid.log
包含详细的堆栈跟踪,在这里我们可以在底部找到解释的帧,在中间找到C1帧,最后在顶部找到C2帧。

    Java frames: (J=compiled Java pre, j=interpreted, Vv=VM pre)J 164 C2 Main.f()V (12 bytes) @ 0x00007f21251a5958 [0x00007f21251a5900+0x0000000000000058]J 164 C2 Main.f()V (12 bytes) @ 0x00007f21251a5920 [0x00007f21251a5900+0x0000000000000020]  // ... repeated 19787 times ...J 164 C2 Main.f()V (12 bytes) @ 0x00007f21251a5920 [0x00007f21251a5900+0x0000000000000020]J 163 C1 Main.f()V (12 bytes) @ 0x00007f211dca50ec [0x00007f211dca5040+0x00000000000000ac]J 163 C1 Main.f()V (12 bytes) @ 0x00007f211dca50ec [0x00007f211dca5040+0x00000000000000ac]  // ... repeated 1866 times ...J 163 C1 Main.f()V (12 bytes) @ 0x00007f211dca50ec [0x00007f211dca5040+0x00000000000000ac]j  Main.f()V+8j  Main.f()V+8  // ... repeated 1839 times ...j  Main.f()V+8j  Main.main([Ljava/lang/String;)V+0v  ~StubRoutines::call_stub


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/404316.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号