如何在JVM HotSpot中使用-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print选项

20

它看起来不太难尝试。 - Oleg Mikheev
当然我已经尝试过,但它们不起作用,我的问题是如何使它们工作?我会进行编辑。 - alain.janinm
你尝试过使用“如果没有输出,请使用-XX:+PrintCompilation来验证您的方法是否被编译了吗?” - Peter Svensson
是的,它什么也不做,只是一行数字,就像上面的选项一样。顺便说一下,这个文档是针对OpenJDK的。我认为它在Java HotSpot上不起作用。我会尝试找到一个等效的方案。 - alain.janinm
2个回答

28

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

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

你应该能够看到 UnlockDiagnosticVMOptions, CompileCommandPrintAssembly 选项可用。使用 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构建环境已经被证明很困难。

我们已经确认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 <sysdep.h>
+#include <errno.h>
 #include <libiberty.h>
 #include <bfd.h>
 #include <dis-asm.h>

运行make成功后,现在只需将hsdis-i386.so插件复制到hsdis build目录下的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;
    }
}

非常感谢您的回复,我在理解hsdis的来源和安装方法方面遇到了问题。我不明白应该在哪里创建这个构建目录。目前,我已经从这里下载了文件http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/,并且我已经下载了binutils-2.22。当您提到“binutils”时,是指整个目录还是只是名为binutils的子目录? - alain.janinm
从hsdis自述文件中可以看到,makefile会在build/binutils目录中查找源代码,或者您可以使用BINTUILS=path指定其位置。build/binutils是相对于hsdis文件所在的目录的。从该目录,您可以尝试类似这样的操作:mkdir build; cd build; wget -O - http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2 | tar xjvf -; ln -s binutils-2.2.22 binutils。或者尝试BINUTILS=path_to_where_you_extracted_binutils make。 - Go Dan
@DanCruz - 我认为最后一行应该是ln -s binutils-2.22 binutils而不是ln -s binutils-2.2.22 binutils,但除此之外,非常棒。 - BeeOnRope
@DanCruz 我尝试编译它,但是我遇到了一个编译器错误 "bad reloc address 0x0"。你是否也遇到了这样的问题?如果是的话,你可以在这里回答我的问题https://dev59.com/sHvZa4cB1Zd3GeqP_iVX。 - René Link

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

$ 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++) {...}, 这就是为什么在我们的情况下,如果没有100万次迭代,我们需要-XX:CompileThreshold=1选项(在-server模式下默认为10000)才能查看我们反汇编的编译代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接