JVM 解释和编译指南( 三 )

可以看到在第五次迭代之后,代码片段被 JIT 编译了:
--------------------------------5 iteration21456bDemo::<init> (5 bytes)21557bDemo::square (16 bytes)Square(i) = 25Time taken= 983002--------------------------------可以看到 , 与 square() 方法一起,构造方法也被 JIT 编译了 。在 for 循环中调用 square() 之前要先构造 Demo 实例,所以构造方法的解释次数同样达到 JIT 编译阈值 。这个例子说明了在解释发生之后何时 JIT 会介入 。
要查看编译后的代码,需要使用 -XX:+PrintAssembly 标志,该标志仅在库路径中有反汇编器时才起作用 。对于 OpenJDK,使用 hsdis 作为反汇编器 。下载合适版本的反汇编程序库,在本例中是 hsdis-amd64.so,并将其放在 Java_HOME/lib/server 目录下 。使用时还需要在 -XX:+PrintAssembly 之前增加 -XX:+UnlockDiagnosticVMOptions 选项 。否则,JVM 会给你一个警告 。
完整命令如下:
$ java -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler-XX:-TieredCompilation -XX:CompileThreshold=5 -XX:+UnlockDiagnosticVMOptions-XX:+PrintAssembly Demo[...]5 iteration17856bDemo::<init> (5 bytes)Compiled method (c2)17856Demo::<init> (5 bytes) total in heap[0x00007fd4d08dad10,0x00007fd4d08dafe0] = 720 relocation[0x00007fd4d08dae88,0x00007fd4d08daea0] = 24[...] handler table[0x00007fd4d08dafc8,0x00007fd4d08dafe0] = 24[...] dependencies[0x00007fd4d08db3c0,0x00007fd4d08db3c8] = 8 handler table[0x00007fd4d08db3c8,0x00007fd4d08db3f8] = 48----------------------------------------------------------------------Demo.square(I)I[0x00007fd4d08db1c0, 0x00007fd4d08db2b8]248 bytes[Entry Point][Constants]# {method} {0x00007fd4b841f4b0} 'square' '(I)I' in 'Demo'# this:rsi:rsi= 'Demo'# parm0:rdx= int#[sp+0x20](sp of caller)[...][Stub Code]0x00007fd4d08db280: movabs $0x0,%rbx;{no_reloc}0x00007fd4d08db28a: jmpq0x00007fd4d08db28a;{runtime_call}0x00007fd4d08db28f: movabs $0x0,%rbx;{static_stub}0x00007fd4d08db299: jmpq0x00007fd4d08db299;{runtime_call}[Exception Handler]0x00007fd4d08db29e: jmpq0x00007fd4d08bb880;{runtime_call ExceptionBlob}[Deopt Handler Code]0x00007fd4d08db2a3: callq0x00007fd4d08db2a80x00007fd4d08db2a8: subq$0x5,(%rsp)0x00007fd4d08db2ad: jmpq0x00007fd4d08a01a0;{runtime_call DeoptimizationBlob}0x00007fd4d08db2b2: hlt0x00007fd4d08db2b3: hlt0x00007fd4d08db2b4: hlt0x00007fd4d08db2b5: hlt0x00007fd4d08db2b6: hlt0x00007fd4d08db2b7: hltImmutableOopMap{rbp=NarrowOop }pc offsets: 96ImmutableOopMap{}pc offsets: 112ImmutableOopMap{rbp=Oop }pc offsets: 148 Square(i) = 25Time taken= 2567698--------------------------------6 iterationSquare(i) = 36Time taken= 76752[...]--------------------------------10 iterationSquare(i) = 100Time taken= 52888我只截取了输出中与 Demo.java 相关的部分 。
现在再来看看 AOT 编译 。它是在 JDK9 中引入的特性 。AOT 是用于生成 .so 这样的库文件的静态编译器 。用 AOT 可以将指定的类编译成 .so 库 。这个库可以直接执行,而不用解释或 JIT 编译 。如果 JVM 没有检测到 AOT 编译的代码,它会进行常规的解释和 JIT 编译 。
使用 AOT 编译的命令如下:
$ jaotc --output=libDemo.so Demo.class用下面的命令来查看共享库的符号表:
【JVM 解释和编译指南】$ nm libDemo.so要使用生成的 .so 库,使用 -XX:+UnlockExperimentalVMOptions 和 -XX:AOTLibrary
$ java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libDemo.so Demo1 iterationSquare(i) = 1Time taken= 7831139--------------------------------2 iterationSquare(i) = 4Time taken= 36619[...]10 iterationSquare(i) = 100Time taken= 42085从输出上看,跟完全用解释的情况没有区别 。为了确认 AOT 发挥了作用,使用 -XX:+PrintAOT
$ java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libDemo.so -XX:+PrintAOT Demo281loaded./libDemo.soaot library801aot[ 1]Demo.main([Ljava/lang/String;)V802aot[ 1]Demo.square(I)I803aot[ 1]Demo.<init>()V1 iterationSquare(i) = 1Time taken= 7252921--------------------------------2 iterationSquare(i) = 4Time taken= 57443[...]10 iterationSquare(i) = 100Time taken= 53586要确认没有发生 JIT 编译 , 用如下命令:
$ java -XX:+UnlockExperimentalVMOptions -Xbatch -XX:+PrintCompilation-XX:CompileCommandFile=hotspot_compiler -XX:-TieredCompilation-XX:CompileThreshold=3 -XX:AOTLibrary=./libDemo.so -XX:+PrintAOT Demo191loaded./libDemo.soaot library771aot[ 1]Demo.square(I)I772aot[ 1]Demo.main([Ljava/lang/String;)V773aot[ 1]Demo.<init>()V772aot[ 1]Demo.main([Ljava/lang/String;)Vmade not entrant[...]4 iterationSquare(i) = 16Time taken= 43366[...]10 iterationSquare(i) = 100Time taken= 59554


推荐阅读