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

als*_*net 77 java assembly jit jvm

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

ass*_*ias 73

一般用法

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

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
Run Code Online (Sandbox Code Playgroud)

过滤特定方法

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

-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod
Run Code Online (Sandbox Code Playgroud)

笔记:

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

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

如果您运行的是Windows,则此页面包含有关如何构建和安装hsdis-amd64.dll以及hsdis-i386.dll使其工作所需的说明.我们在下面复制并扩展该页面的内容*以供参考:


哪里可以获得预建的二进制文件

您可以从fcml项目下载Windows的预构建二进制文件

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

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

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

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

  3. 下载最新的GNU binutils源包并将其内容解压缩到Cygwin主目录.在撰写本文时,最新的软件包是binutils-2.25.tar.bz2.这应该会导致binutils-2.25Cygwin主目录中名为(或最新版本)的目录.
  4. 通过转到JDK 8 Updates存储库,选择与已安装的JRE版本对应的标记,然后单击bz2,下载OpenJDK源.将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.编辑<Cygwin home directory>\hsdis\build\Linux-amd64\bfd\Makefile在像写字板或记事本的文本编辑器++改变SUBDIRS = doc po(线342,如果使用的binutils 2.25)到SUBDIRS = po.重新运行上一个命令.

该DLL现在可以通过从复制它被安装hsdis\build\Linux-amd64hsdis\build\Linux-i586您的JRE bin\serverbin\client目录.您可以通过搜索找到系统上的所有此类目录java.dll.

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

*页面许可证是Creative Commons


Fal*_*ina 45

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

-XX:+ PrintOptoAssembly

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

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

-XX:CompileThreshold =#

当你运行你的代码时.

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

  • 不应该(现在)-XX:+ PrintAssembly,至少现在?在我的机器上测试,并匹配这里所说的内容:http://wikis.sun.com/display/HotSpotInternals/PrintAssembly在此选项和反汇编程序插件之前,您需要-XX:+ UnlockDiagnosticVMOptions. (3认同)
  • 是的,需要DEBUG二进制文件.http://blogs.warwick.ac.uk/richardwarburton/entry/hotspot_print_assembly/ (2认同)

swo*_*iak 28

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

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

要在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
Run Code Online (Sandbox Code Playgroud)

其他配置参数:

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

对于Windows,Intel语法是默认语法,而AT&T语言是GNU/Linux的默认语法.

有关更多详细信息,请参阅" FCML库参考手册"

  • 只是一点补充:在一些 linux 发行版上,你可以只安装一个包,例如在 Ubuntu 中:`apt-get install libhsdis0-fcml` (https://askubuntu.com/a/991166/489909)。自己构建它可能没有必要。 (2认同)

小智 8

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

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

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


And*_*ach 5

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

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

    0008fba8 7c90e9c0 NTDLL!KiFastSystemCallRet
    0008fbac 7c8025cb NTDLL!ZwWaitForSingleObject + 0xC的
    0008fc10 7c802532 KERNEL32!WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 KERNEL32!WaitForSingleObject的+ 0×12
    0008fc40 00402f68的java + 0x3a13
    0008fee4 004087b8的java + 0x2f68
    0008ffc0 7c816fd7的java + 0x87b8

    0008fff0 00000000 KERNEL32!BaseProcessStart + 0×23

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

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

  • 在WinDBG上:
    单击"查看" - >"反汇编".
    单击编辑 - >转到地址.
    00402f68放在那里
    得到

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

有关其他信息,请参阅示例如何使用process explorer和WinDbg从内存转储中追溯JIT-ed代码.