HSDIS 是一个被官方推荐的 HotSpot 虚拟机即时编译代码的反汇编插件,它可以让 Hotspot 提供的 -XX:+PrintAssembly 指令调用它来把即时编译器动态生成的本地机器码还原为汇编代码输出,同时还会自动产生大量非常有价值的注释,这样我们就可以通过输出的汇编代码来从最本质的角度分析问题。
使用说明
这里提供一个 MacOS 下已经编译好了的插件。下载后直接放到 JDK_HOME/jre/lib 目录(Mac OS)中即可使用。我们用下面的测试代码简单演示下如何使用这个插件:
/**
* -XX:+UnlockDiagnosticVMOptions
* -XX:+PrintAssembly -Xcomp
* -XX:CompileCommand=dontinline,*HSDIS_Test.sum
* -XX:CompileCommand=compileonly,*HSDIS_Test.sum
*/
public class HSDIS_Test {
int a = 1;
static int b = 2;
public int sum(int c) {
return a + b + c;
}
public static void main(String[] args) {
new HSDIS_Test().sum(3);
}
}
通过 javac 编译这段代码,并使用如上代码注释中的命令参数启动执行。其中 -Xcomp 是让虚拟机以编译模式执行代码,这样不需要执行足够次数来预热就能触发即时编译。两个 -XX:CompileCommand 的意思是让编译器不要内联 sum() 方法并且只编译 sum() 方法,-XX:+PrintAssembly 就是输出反汇编内容。
[Verified Entry Point]
0x0000000113a98460: mov %eax,-0x14000(%rsp)
0x0000000113a98467: push %rbp
0x0000000113a98468: sub $0x30,%rsp
0x0000000113a9846c: movabs $0x12c2f0560,%rax ; {metadata(method data for {method} {0x000000012c2f02a0} 'sum' '(I)I' in 'HSDIS_Test')}
0x0000000113a98476: mov 0xdc(%rax),%edi
0x0000000113a9847c: add $0x8,%edi
0x0000000113a9847f: mov %edi,0xdc(%rax)
0x0000000113a98485: movabs $0x12c2f02a0,%rax ; {metadata({method} {0x000000012c2f02a0} 'sum' '(I)I' in 'HSDIS_Test')}
0x0000000113a9848f: and $0x0,%edi
0x0000000113a98492: cmp $0x0,%edi
0x0000000113a98495: je 0x0000000113a984bb ;*aload_0
; - HSDIS_Test::sum@0 (line 13)
......
如果编译报错:找不到或无法加载主类,则可能是 Package 路径问题,去掉 Package 重新编译即可。
JITWatch
上面的例子由于代码比较简单,肉眼直接看日志中的汇编输出就可以了。但在正式环境中 -XX:+PrintAssembly 的日志输出量巨大,且难以和代码对应起来,这就必须使用工具来辅助了。
JITWatch 是 HSDIS 经常搭配使用的可视化的编译日志分析工具,为便于在 JITWatch 中读取,我们可使用以下参数把日志输出到 log 文件:
-XX:+UnlockDiagnosticVMOptions
-XX:+TraceClassLoading
-XX:+LogCompilation
-XX:LogFile=logfile.log
-XX:+PrintAssembly
启动 JITWatch 要先在 github 上下载源代码,然后使用 maven 命令编译,最后运行 launchUI.sh 即可。
在 JITWatch 中加载日志后,就可以看到执行期间使用过的各种对象类型和对应调用过的方法了,加载界面如下图所示:
选择想要查看的类和方法,即可查看对应的 Java 源代码、字节码和即时编译器生成的汇编代码。