如何在JVM中查看JIT编译的代码?

时间:2009-10-01 11:45:16

标签: java assembly jvm jit

有没有办法在JVM中看到JIT生成的本机代码?

7 个答案:

答案 0 :(得分:68)

一般用法

如其他答案所述,您可以使用以下JVM选项运行:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

过滤特定方法

您还可以使用以下语法过滤特定方法:

-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod

注意:

  • 您可能需要根据操作系统等将第二个参数放在引号内。
  • 如果方法被内联,您可能会错过一些优化

如何:在Windows上安装所需的库

如果您运行的是Windows,则this page会提供有关如何构建和安装hsdis-amd64.dllhsdis-i386.dll以使其正常运行所需的说明。我们在下面复制并扩展该页面的内容*以供参考:


从哪里获得预建二进制文件

您可以从fcml项目

下载适用于Windows的预建二进制文件

如何在Windows上构建hsdis-amd64.dllhsdis-i386.dll

此版本的指南是使用64位Cygwin在Windows 8.1 64位上编写的,并生成hsdis-amd64.dll

  1. Install Cygwin。在Select Packages屏幕上,添加以下软件包(通过展开Devel类别,然后在每个软件包名称旁边的Skip标签上单击一次):

    • make
    • mingw64-x86_64-gcc-core(仅hsdis-amd64.dll
    • 需要
    • mingw64-i686-gcc-core(仅hsdis-i386.dll
    • 需要
    • diffutilsUtils类别)
  2. 运行Cygwin终端。这可以使用安装程序创建的桌面或开始菜单图标来完成,并将创建您的Cygwin主目录(默认为C:\cygwin\home\<username>\C:\cygwin64\home\<username>\)。

  3. Download the latest GNU binutils source package并将其内容提取到您的Cygwin主目录。在撰写本文时,最新的包是binutils-2.25.tar.bz2。这应该会导致Cygwin主目录中名为binutils-2.25的目录(或任何最新版本)。
  4. going to the JDK 8 Updates repository下载OpenJDK源代码,选择与已安装的JRE版本对应的代码,然后单击bz2。将hsdis目录(在src\share\tools中找到)解压缩到您的Cygwin主目录。
  5. 在Cygwin终端中,输入cd ~/hsdis
  6. 要构建hsdis-amd64.dll,请输入

    make OS=Linux MINGW=x86_64-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    要构建hsdis-i386.dll,请输入

    make OS=Linux MINGW=i686-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    在任何一种情况下,将2.25替换为您下载的binutils版本。 OS=Linux是必要的,因为尽管Cygwin是一个类似Linux的环境,但hsdis makefile却无法识别它。

  7. 构建将失败,邮件为./chew: No such file or directorygcc: command not found。在Wordpad或Notepad ++等文本编辑器中编辑<Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile,将SUBDIRS = doc po(第342行,如果使用binutils 2.25)更改为SUBDIRS = po。重新运行上一个命令。
  8. 现在可以通过将DLL从hsdis\build\Linux-amd64hsdis\build\Linux-i586复制到JRE的bin\serverbin\client目录来安装DLL。您可以通过搜索java.dll找到系统中的所有此类目录。

    额外提示:如果您更喜欢英特尔ASM语法到AT&amp; T,请指定-XX:PrintAssemblyOptions=intel以及您使用的任何其他PrintAssembly选项。

    *页面许可证是Creative Commons

答案 1 :(得分:41)

假设您正在使用Sun Hotspot JVM(即Oracle提供的java.com上的JVM),您可以添加标记

  

-XX:+ PrintOptoAssembly

运行代码时。这将打印出JIT编译器生成的优化代码,并将其余部分排除在外。

如果要查看整个字节码,包括未经优化的部分,请添加

  

-XX:CompileThreshold =#

当您运行代码时。

您可以阅读有关此命令和JIT功能的更多信息here

答案 2 :(得分:25)

您需要一个hsdis插件才能使用PrintAssembly。一个方便的选择是基于FCML库的hsdis插件。

可以为类UNIX系统编译,在Windows上,您可以使用Sourceforge上的FCML download部分中提供的预构建库:

要在Windows中安装:

  • 提取dll(可以在hsdis-1.1.2-win32-i386.zip和hsdis-1.1.2-win32-amd64.zip中找到)。
  • 将dll复制到存在的地方java.dll(使用Windows搜索)。在我的系统上,我在两个位置找到它:
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

要在Linux中安装:

  • 下载源代码,解压缩
  • cd <source code dir>
  • ./configure && make && sudo make install
  • cd example/hsdis && make && sudo make install
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/lib/amd64/hsdis-amd64.so
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/jre/lib/amd64/hsdis-amd64.so
  • 在我的系统上,JDK位于/usr/lib/jvm/java-8-oracle

如何运行它:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly 
-XX:+LogCompilation -XX:PrintAssemblyOptions=intel,mpad=10,cpad=10,code 
-jar fcml-test.jar

其他配置参数:

代码在助记符之前打印机器代码 intel 使用Intel语法 gas 使用AT&amp; T汇编语法(兼容GNU汇编程序) dec 以十进制值打印IMM和位移 mpad = XX 填充指令的助记符部分。
cpad = XX 填写机器代码。
seg 显示默认的段寄存器 zeros 在HEX文字的情况下显示前导零。

对于Windows,英特尔语法是默认语法,而AT&amp; T语言是GNU / Linux的默认语法。

有关详细信息,请参阅FCML Library Reference Manual

答案 3 :(得分:8)

对于HotSpot(是Sun)JVM,即使在产品模式下:

http://wikis.oracle.com/display/HotSpotInternals/PrintAssembly

需要一些程序集:它需要一个插件。

答案 4 :(得分:5)

我相信如果你在Windows机器上运行WinDbg会有所帮助。 我刚跑了一个罐子。

  • 然后我附加了java进程 通过 Windbg
  • 通过命令检查线程;有11个线程,0个线程是主要工作线程
  • 切换到0线程 - ~0s
  • 通过 kb 查看未受损的callstack,有:

    0008fba8 7c90e9c0 ntdll!KiFastSystemCallRet
    0008fbac 7c8025cb ntdll!ZwWaitForSingleObject + 0xc
    0008fc10 7c802532 kernel32!WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 kernel32!WaitForSingleObject + 0x12
    0008fc40 00402f68 java + 0x3a13
    0008fee4 004087b8 java + 0x2f68
    0008ffc0 7c816fd7 java + 0x87b8

    0008fff0 00000000 kernel32!BaseProcessStart + 0x23

突出显示的行是在JVM上直接运行JIT编码。

  • 然后我们可以找到方法地址:
    java + 0x2f68是00402f68

  • 在WinDBG上:
    点击查看 - &gt; 拆卸。
    点击编辑 - &gt;去 地址。
    00402f68 放在那里 并得到了

    00402f68 55推ebp
    00402f69 8bec mov ebp,esp
    00402f6b 81ec80020000 sub esp,280h
    00402f71 53推ebx
    00402f72 56推esi
    00402f73 57推edi
    ...... 等等

有关其他信息,请参阅Example如何使用process explorer和WinDbg从内存转储中追溯JIT编码。

答案 5 :(得分:4)

查看机器代码和一些性能数据的另一种方法是使用AMD的CodeAnalyst或OProfile,它有一个Java插件,可以将执行的Java代码可视化为机器代码。

答案 6 :(得分:0)

使用JMH的perfasm探查器(LinuxPerfAsmProfilerWinPerfAsmProfiler)打印热点的程序集。 JMH确实需要hsdis库,因为它依赖于PrintAssembly