【问题标题】:How to see JIT-compiled code in JVM?如何在 JVM 中查看 JIT 编译的代码?
【发布时间】:2010-12-02 23:22:31
【问题描述】:

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

【问题讨论】:

  • 您确定要查看 JIT 编译的(本机)代码,还是只查看字节码?我之所以问,是因为在这里问这个问题会让人怀疑你是否真的想看原生代码……而且,对不起,我也不知道这样的工具。
  • 我想查看精确的 JIT 编译的本机代码。当然,这不是我需要完成的工作,而是一种实验和调查。
  • 次要框架挑战:现代 JVM 中使用的动态编译器不只有 一个 版本的编译代码;它可能开始解释,然后编译一个方法或只是它的一部分,然后可能会随着类的加载/卸载或使用模式的变化或基于性能统计数据而重新编译它多次。 (我认为它甚至可以丢弃已编译的版本并返回到解释,如果这看起来有益的话。)所以你可能不仅在不同的机器上得到不同的代码,甚至在同一台机器上的不同运行,但在不同的时间在 相同运行。

标签: java assembly jvm jit


【解决方案1】:

一般用法

正如其他答案所解释的,您可以使用以下 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 位 Windows 8.1 上使用 64 位 Cygwin 并生成 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需要)
    • diffutils(在Utils 类别中)
  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版本对应的tag,点击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。重新运行之前的命令。

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

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

*页面许可为知识共享

【讨论】:

  • 为其他平台预构建的二进制文件 - kenai.com/projects/base-hsdis/downloads
  • @AshwinJayaprakash 我应该将这些文件放在 Mac OS 的什么位置?
  • @KorayTugay 将它们放入/usr/lib/
  • 我已经通过从链接到页面的最新版本复制来更新答案,但这突出了我们通常链接到外部资源而不是逐字复制它们的原因。
  • @AleksandrDubinsky 感谢您的更新。我故意复制它:如果该网站被关闭,我的答案仍然是独立的......
【解决方案2】:

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

-XX:+PrintOptoAssembly

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

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

-XX:CompileThreshold=#

当你运行你的代码时。

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

【讨论】:

  • 这个选项是否只存在于调试版本或任何东西中?因为我的 JVM(“Java(TM) SE 运行时环境(build 1.6.0_16-b01”)无法识别它,即使网络上的源代码表明该功能在 Sun Java 6 和 OpenJDK 中可用。
  • 是的,需要调试二进制文件。 blogs.warwick.ac.uk/richardwarburton/entry/…
  • 不应该是(现在)-XX:+PrintAssembly,至少现在是这样吗?在我的机器上测试过,与这里所说的相符:wikis.sun.com/display/HotSpotInternals/PrintAssembly 在此选项之前需要 -XX:+UnlockDiagnosticVMOptions 和反汇编插件。
  • @Blaisorblade 我得到:错误指定的 VM 选项“PrintAssembly”错误:无法创建 Java 虚拟机。错误:发生了致命异常。程序将退出。
  • @KorayTugay 查看其他答案 — 更新后的链接是 wikis.oracle.com/display/HotSpotInternals/PrintAssembly,如 stackoverflow.com/a/15146962/53974stackoverflow.com/a/4149878/53974 中所示。如果以下说明不起作用,请在适当的地方询问详细信息(不确定是否应该为您的案例提出另一个问题,参考这个)。
【解决方案3】:

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

其他配置参数:

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

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

更多详情请见FCML Library Reference Manual

【讨论】:

  • 感谢您修复库。它在 Linux 上也很好用。我正在删除我的旧 cmets 以保持混乱。
  • 在Linux上,安装libhsdis.so并软链接到hsdis-amd64.so后,我运行java命令,提示找不到hsdis-amd64.so。我重新启动然后重新运行java,没关系。如何避免重新启动以使软链接立即生效?退出?
  • 只是一点点补充:在某些 linux 发行版上,您可以只安装一个包,例如在 Ubuntu 中:apt-get install libhsdis0-fcml (askubuntu.com/a/991166/489909)。可能没有必要自己构建它。
【解决方案4】:

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

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

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

【讨论】:

  • 您的答案中的链接似乎不见了。请您更新一下吗?提前谢谢。
【解决方案5】:

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

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

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

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

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

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

如需了解更多信息,请参阅Example 如何使用进程资源管理器和 WinDbg 从内存转储中追溯 JIT 代码。

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

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

      【讨论】:

        猜你喜欢
        • 2011-01-25
        • 1970-01-01
        • 2010-12-31
        • 2015-03-23
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        相关资源
        最近更新 更多