如何使用-XX:+ UnlockDiagnosticVMOptions -XX:CompileCommand =使用JVM HotSpot打印选项

我正在尝试使用-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod命令行,如本文所述。

它似乎可以通过open-jdk ( https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly )获得。

如何在oracle JDK7和JVM HotSpot中使用这些选项(或类似的等价物)?

这些说明适用于Linux(Ubuntu 10.04.4 LTS),但应适用于您的操作系统。 下载Oracle JDK 7u3并适当设置JAVA_HOMEPATH环境变量后,执行以下命令检查可用选项:

 java -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version 

您应该看到UnlockDiagnosticVMOptionsCompileCommandPrintAssembly选项可用。 使用CompileCommand选项还将启用PrintAssembly选项。 但是,您需要使用PrintAssembly的HotSpot反汇编程序插件才能工作; 没有它,你可能会看到如下内容:

 $ java -version java version "1.7.0_03" Java(TM) SE Runtime Environment (build 1.7.0_03-b04) Java HotSpot(TM) Server VM (build 22.1-b02, mixed mode) $ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main CompilerOracle: print *Main.main Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output Compiled method (c2) 68 1 % Main::main @ 4 (49 bytes) total in heap [0xb3a97548,0xb3a979ec] = 1188 relocation [0xb3a97610,0xb3a97624] = 20 main code [0xb3a97640,0xb3a97840] = 512 stub code [0xb3a97840,0xb3a97850] = 16 oops [0xb3a97850,0xb3a97858] = 8 scopes data [0xb3a97858,0xb3a97898] = 64 scopes pcs [0xb3a97898,0xb3a979e8] = 336 dependencies [0xb3a979e8,0xb3a979ec] = 4 Could not load hsdis-i386.so; library not loadable; PrintAssembly is disabled OopMapSet contains 1 OopMaps 

要获得HotSpot反汇编程序插件,您需要构建它。 看看OpenJDK 7u2源代码, hsdis插件自述文件说:

要将插件与JVM一起使用,您需要一个可以加载它的新版本。 如果JVM的产品模式不接受-XX:+ PrintAssembly,则表明您没有足够新的版本。

要构建这个项目,你需要一个GNU binutils的副本来构建。

从理论上讲,这应该可以在Windows上构建,但在Windows上获得一个有效的GNU构建环境已经certificate是困难的。

我们已在上面确认Oracle JDK 7u3支持PrintAssembly 。 我按照hsdis插件自述文件说明,下载了GNU binutils 2.22,将它放在hsdis build/binutils目录下并运行make 。 这最终产生了以下错误:

 hsdis.c:32:20: error: sysdep.h: No such file or directory 

为了解决这个问题,我使用以下补丁更改了hsdis.c:

 diff -r 6259c6d3bbb7 src/share/tools/hsdis/hsdis.c --- a/src/share/tools/hsdis/hsdis.c Mon Dec 12 23:08:01 2011 -0800 +++ b/src/share/tools/hsdis/hsdis.c Thu Feb 23 09:26:37 2012 -0500 @@ -29,7 +29,7 @@ #include "hsdis.h" -#include  +#include  #include  #include  #include  

然后运行make成功。 现在只需将hsdis build目录中的hsdis-i386.so插件复制到Oracle JDK 7u3 jre/lib/i386目录即可。

现在您可以看到反汇编的编译代码:

 $ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main CompilerOracle: print *Main.main Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output Compiled method (c2) 68 1 % Main::main @ 4 (49 bytes) total in heap [0xb3999548,0xb39999ec] = 1188 relocation [0xb3999610,0xb3999624] = 20 main code [0xb3999640,0xb3999840] = 512 stub code [0xb3999840,0xb3999850] = 16 oops [0xb3999850,0xb3999858] = 8 scopes data [0xb3999858,0xb3999898] = 64 scopes pcs [0xb3999898,0xb39999e8] = 336 dependencies [0xb39999e8,0xb39999ec] = 4 Loaded disassembler from [snip]/jdk1.7.0_03/jre/lib/i386/hsdis-i386.so Decoding compiled method 0xb3999548: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] [Constants] # {method} 'main' '([Ljava/lang/String;)V' in 'Main' 0xb3999640: call 0xb6ff8510 ; {runtime_call} 0xb3999645: data32 xchg %ax,%ax 0xb3999648: mov %eax,-0x3000(%esp) 0xb399964f: push %ebp 0xb3999650: sub $0x38,%esp 0xb3999656: mov %ecx,%esi 0xb3999658: mov 0x4(%esi),%ebp 0xb399965b: mov 0x8(%esi),%edi 0xb399965e: mov (%ecx),%esi 0xb3999660: mov %ecx,(%esp) 0xb3999663: call 0xb7078cf0 ;*iload_3 [snip] 0xb399983e: hlt 0xb399983f: hlt [Exception Handler] [Stub Code] 0xb3999840: jmp 0xb39981e0 ; {no_reloc} [Deopt Handler Code] 0xb3999845: push $0xb3999845 ; {section_word} 0xb399984a: jmp 0xb397e220 ; {runtime_call} 0xb399984f: .byte 0x0 OopMapSet contains 1 OopMaps #0 OopMap{off=468} 

我使用的测试类是:

 public class Main { public static void main(final String[] args) { long x = 0; for (int i = 0; i < 1000000; i++) { x += calculate(i); } System.out.println("x=" + x); } private static long calculate(final int i) { return (long)i * (long)i; } } 

在我的情况下,看到反汇编的编译代码:

 $ java -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand="compileonly pac/kage/MyClass myMethod" MyClass 

在上面的例子中有循环: for (int i = 0; i < 1 000 000; i++) {...} ,这就是为什么在没有1 000 000次迭代的情况下我们需要-XX:CompileThreshold=1选项(通过默认10 000 for -server)查看我们的反汇编编译代码。