90

有什么方法可以查看 JIT 在 JVM 中生成的本机代码吗?

4

7 回答 7

81

一般用法

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

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

过滤特定方法

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

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

笔记:

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

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

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


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

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

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

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

  3. 下载最新的 GNU binutils 源包并将其内容解压缩到 Cygwin 主目录。在撰写本文时,最新的软件包是binutils-2.25.tar.bz2. 这应该会在 Cygwin 主目录中生成一个名为binutils-2.25(或任何最新版本)的目录。
  4. 通过转到 JDK 8 Updates 存储库下载 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. 重新运行之前的命令。

现在可以通过从JREhsdis\build\Linux-amd64或目录复制它来安装 DLL。您可以通过搜索找到系统上的所有此类目录。hsdis\build\Linux-i586bin\serverbin\clientjava.dll

额外提示:如果您更喜欢 Intel ASM 语法而不是 AT&T,-XX:PrintAssemblyOptions=intel请在您使用的任何其他 PrintAssembly 选项旁边指定。

*页面许可是知识共享

于 2013-02-28T22:31:54.297 回答
46

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

-XX:+PrintOptoAssembly

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

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

-XX:编译阈值=#

当你运行你的代码时。

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

于 2009-10-01T13:20:49.630 回答
29

您需要一个 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

附加配置参数:

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

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

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

于 2014-07-02T06:28:30.523 回答
8

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

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

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

于 2010-11-10T22:48:43.790 回答
5

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

  • 然后我通过Windbg附加到java进程
  • 通过~命令检查线程;有11个线程,0个线程是主工作线程
  • 切换到 0 线程 - ~0s
  • 通过kb查看未管理的调用堆栈有:

    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-ed 代码。

  • 然后我们可以查找方法地址:
    java+0x2f68 is 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
    ...等等

有关其他信息,这里是如何使用进程资源管理器和 WinDbg 从内存转储中追溯 JIT 代码的示例。

于 2009-10-01T13:14:40.210 回答
4

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

于 2010-11-10T22:23:22.637 回答
1

使用 JMH 的 perfasm 分析器 (LinuxPerfAsmProfilerWinPerfAsmProfiler) 打印您的热点组件。JMH 确实需要该hsdis库,因为它依赖于PrintAssembly.

于 2015-05-24T03:25:10.300 回答