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

有没有什么方法可以查看JVM在JVM中生成的本地代码?

假设您正在使用Sun Hotspot JVM。 添加标志

-XX:+ PrintOptoAssembly

无论你在运行什么。 这只会打印已被JIT的代码的程序集(即,你不会看到程序集非JIT的东西),但我认为这就是你想要的。 如果你想看看什么都可以,如果它被JIT'd你可以通过调整JIT的门槛:

-XX:CompileThreshold =#

一般用法

正如其他答案所解释的,您可以使用以下JVM选项运行:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly 

过滤一个特定的方法

您还可以使用以下语法筛选特定的方法:

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

笔记:

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

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

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


在哪里获得预编译的二进制文件

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

  • hsdis-amd64.dll
  • hsdis-i386.dll

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

本指南的版本是在Windows 8.1 64位上使用64位Cygwin编写的,并生成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. 运行Cygwinterminal。 这可以使用安装程序创build的桌面或开始菜单图标完成,并将创build您的Cygwin主目录(默认为C:\cygwin\home\<username>\C:\cygwin64\home\<username>\ ) 。

  3. 下载最新的GNU binutils源码包并将其内容提取到您的Cygwin主目录。 在撰写本文时,最新的软件包是binutils-2.25.tar.bz2 。 这应该导致一个名为binutils-2.25 (或者最新版本)的目录在你的Cygwin主目录下。
  4. 通过转至JDK 8更新存储库 ,select与已安装的JRE版本对应的标签,然后单击bz2,下载OpenJDK源代码。 提取hsdis目录(在src\share\tools )到你的Cygwin主目录。
  5. 在Cygwinterminal中,inputcd ~/hsdis
  6. 要构buildhsdis-amd64.dll ,请input

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

    要构buildhsdis-i386.dll ,请input

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

    在任何一种情况下,用您下载的binutils版本replace2.25OS=Linux是必要的,因为尽pipeCygwin是类似Linux的环境,但hsdis makefile却无法识别它。

  7. ./chew: No such file or directory将失败并显示消息./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 。 重新运行先前的命令。

现在可以通过将hsdis\build\Linux-amd64hsdis\build\Linux-i586复制到您的JRE的bin\serverbin\client目录来安装DLL。 您可以通过searchjava.dll在您的系统上find所有这些目录。

奖励提示:如果您更喜欢Intel -XX:PrintAssemblyOptions=intel语法到AT&T,请指定-XX:PrintAssemblyOptions=intel以及您使用的任何其他PrintAssembly选项。

*页面许可是Creative Commons

你需要一个hsdis插件来使用PrintAssembly 。 一个方便的select是基于FCML库的hsdis插件。

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

在Windows中安装:

  • 解压缩dll(可以在hsdis-1.1.2-win32-i386.zip和hsdis-1.1.2-win32-amd64.zip中find)。
  • 将dll复制到存在java.dll地方(使用Windowssearch)。 在我的系统上,我发现它在两个位置:
    • 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 

其他configuration参数:

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

英特尔语法是Windows的默认语法,而AT&T是GNU / Linux的默认语法。

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

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

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

一些组件需要:它需要一个插件。

我相信WinDbg会有帮助,如果你在Windows机器上运行它。 我刚刚运行一个jar子。

  • 然后我通过Windbg连接到java进程
  • 通过命令检查线程; 有11个线程,0个线程是主工作线程
  • 切换到0线程 – 〜0s
  • 通过kb的 unmanmaged 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上:
    点击查看 – >反汇编。
    点击编辑 – >转到地址。
    00402f68放在那里
    得到了

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

有关其他信息,请参阅示例如何使用进程pipe理器和WinDbg从内存转储中追溯JIT编写的代码。

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

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